Browse Source

Merge branch 'f/4-erstellung-der-zu-verwendenden-datenbankstrukturen-2' into 'develop'

Resolve "Erstellung der zu verwendenden Datenbankstrukturen"

Closes #4

See merge request fia72-dev/eva_lernsituation!12
develop
Tobi 7 years ago
parent
commit
d9cdd77df4
  1. 27
      Backend/EVABackend/EVABackend/Areas/Identity/Data/EVABackendIdentityContext.cs
  2. 13
      Backend/EVABackend/EVABackend/Areas/Identity/Data/EVABackendUser.cs
  3. 36
      Backend/EVABackend/EVABackend/Areas/Identity/IdentityHostingStartup.cs
  4. 10
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/AccessDenied.cshtml
  5. 17
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/AccessDenied.cshtml.cs
  6. 12
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/ConfirmEmail.cshtml
  7. 45
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs
  8. 34
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/ExternalLogin.cshtml
  9. 141
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs
  10. 26
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/ForgotPassword.cshtml
  11. 68
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs
  12. 11
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml
  13. 16
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml.cs
  14. 10
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Lockout.cshtml
  15. 18
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Lockout.cshtml.cs
  16. 82
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Login.cshtml
  17. 103
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Login.cshtml.cs
  18. 41
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/LoginWith2fa.cshtml
  19. 99
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs
  20. 29
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml
  21. 90
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs
  22. 10
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Logout.cshtml
  23. 44
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Logout.cshtml.cs
  24. 35
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml
  25. 101
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs
  26. 34
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml
  27. 84
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs
  28. 26
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml
  29. 64
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs
  30. 12
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml
  31. 51
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs
  32. 54
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml
  33. 157
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs
  34. 52
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml
  35. 109
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs
  36. 27
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml
  37. 73
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs
  38. 45
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/Index.cshtml
  39. 148
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs
  40. 46
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs
  41. 27
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml
  42. 34
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs
  43. 24
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml
  44. 61
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml.cs
  45. 35
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml
  46. 93
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs
  47. 56
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml
  48. 72
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs
  49. 22
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/_Layout.cshtml
  50. 14
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml
  51. 10
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml
  52. 1
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml
  53. 37
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Register.cshtml
  54. 99
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Register.cshtml.cs
  55. 37
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/ResetPassword.cshtml
  56. 89
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs
  57. 10
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml
  58. 18
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml.cs
  59. 1
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/_ViewImports.cshtml
  60. 23
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Error.cshtml
  61. 26
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/Error.cshtml.cs
  62. 5
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/_ViewImports.cshtml
  63. 4
      Backend/EVABackend/EVABackend/Areas/Identity/Pages/_ViewStart.cshtml
  64. 194
      Backend/EVABackend/EVABackend/Controllers/EVAController.cs
  65. 45
      Backend/EVABackend/EVABackend/Controllers/ValuesController.cs
  66. 3
      Backend/EVABackend/EVABackend/EVABackend.csproj
  67. 229
      Backend/EVABackend/EVABackend/Migrations/20190612100741_CreateIdentitySchema.Designer.cs
  68. 217
      Backend/EVABackend/EVABackend/Migrations/20190612100741_CreateIdentitySchema.cs
  69. 388
      Backend/EVABackend/EVABackend/Migrations/EVA/20190612101730_InitialCreate.Designer.cs
  70. 403
      Backend/EVABackend/EVABackend/Migrations/EVA/20190612101730_InitialCreate.cs
  71. 386
      Backend/EVABackend/EVABackend/Migrations/EVA/EVAContextModelSnapshot.cs
  72. 227
      Backend/EVABackend/EVABackend/Migrations/EVABackendIdentityContextModelSnapshot.cs
  73. 42
      Backend/EVABackend/EVABackend/Models/Aufnahmeantrag.cs
  74. 13
      Backend/EVABackend/EVABackend/Models/CreateRooms.cs
  75. 227
      Backend/EVABackend/EVABackend/Models/EVAContext.cs
  76. 41
      Backend/EVABackend/EVABackend/Pages/Shared/_CookieConsentPartial.cshtml
  77. 83
      Backend/EVABackend/EVABackend/Pages/Shared/_Layout.cshtml
  78. 25
      Backend/EVABackend/EVABackend/Pages/Shared/_LoginPartial.cshtml
  79. 18
      Backend/EVABackend/EVABackend/Pages/Shared/_ValidationScriptsPartial.cshtml
  80. 5
      Backend/EVABackend/EVABackend/Pages/_ViewImports.cshtml
  81. 3
      Backend/EVABackend/EVABackend/Pages/_ViewStart.cshtml
  82. 6
      Backend/EVABackend/EVABackend/Properties/launchSettings.json
  83. 26
      Backend/EVABackend/EVABackend/ScaffoldingReadme.txt
  84. 11
      Backend/EVABackend/EVABackend/Startup.cs
  85. 7
      Backend/EVABackend/EVABackend/appsettings.json
  86. BIN
      Backend/EVABackend/EVABackend/eva_ls.db
  87. BIN
      Backend/EVABackend/EVABackend/eva_users.db
  88. 37
      Backend/EVABackend/EVABackend/wwwroot/css/site.css
  89. 1
      Backend/EVABackend/EVABackend/wwwroot/css/site.min.css
  90. BIN
      Backend/EVABackend/EVABackend/wwwroot/favicon.ico
  91. 1
      Backend/EVABackend/EVABackend/wwwroot/images/banner1.svg
  92. 1
      Backend/EVABackend/EVABackend/wwwroot/images/banner2.svg
  93. 1
      Backend/EVABackend/EVABackend/wwwroot/images/banner3.svg
  94. 4
      Backend/EVABackend/EVABackend/wwwroot/js/site.js
  95. 1
      Backend/EVABackend/EVABackend/wwwroot/js/site.min.js
  96. 21
      Backend/EVABackend/EVABackend/wwwroot/lib/bootstrap/LICENSE
  97. 587
      Backend/EVABackend/EVABackend/wwwroot/lib/bootstrap/dist/css/bootstrap-theme.css
  98. 1
      Backend/EVABackend/EVABackend/wwwroot/lib/bootstrap/dist/css/bootstrap-theme.css.map
  99. 6
      Backend/EVABackend/EVABackend/wwwroot/lib/bootstrap/dist/css/bootstrap-theme.min.css
  100. 1
      Backend/EVABackend/EVABackend/wwwroot/lib/bootstrap/dist/css/bootstrap-theme.min.css.map
  101. Some files were not shown because too many files have changed in this diff Show More

27
Backend/EVABackend/EVABackend/Areas/Identity/Data/EVABackendIdentityContext.cs

@ -0,0 +1,27 @@
using EVABackend.Areas.Identity.Data;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace EVABackend.Models
{
public class EVABackendIdentityContext : IdentityDbContext<EVABackendUser>
{
public EVABackendIdentityContext(DbContextOptions<EVABackendIdentityContext> options)
: base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Data Source=eva_users.db");
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
}
}

13
Backend/EVABackend/EVABackend/Areas/Identity/Data/EVABackendUser.cs

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
namespace EVABackend.Areas.Identity.Data
{
// Add profile data for application users by adding properties to the EVABackendUser class
public class EVABackendUser : IdentityUser
{
}
}

36
Backend/EVABackend/EVABackend/Areas/Identity/IdentityHostingStartup.cs

@ -0,0 +1,36 @@
using EVABackend.Areas.Identity.Data;
using EVABackend.Models;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
[assembly: HostingStartup(typeof(EVABackend.Areas.Identity.IdentityHostingStartup))]
namespace EVABackend.Areas.Identity
{
public class IdentityHostingStartup : IHostingStartup
{
public void Configure(IWebHostBuilder builder)
{
builder.ConfigureServices((context, services) =>
{
services.AddDbContext<EVABackendIdentityContext>(options =>
options.UseSqlite(
context.Configuration.GetConnectionString("EVABackendIdentityContextConnection")));
services.AddDefaultIdentity<EVABackendUser>()
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<EVABackendIdentityContext>()
.AddDefaultTokenProviders();
services.ConfigureApplicationCookie(options =>
{
options.Cookie.Name = "EVABackend_Token";
});
});
}
}
}

10
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/AccessDenied.cshtml

@ -0,0 +1,10 @@
@page
@model AccessDeniedModel
@{
ViewData["Title"] = "Access denied";
}
<header>
<h1 class="text-danger">@ViewData["Title"]</h1>
<p class="text-danger">You do not have access to this resource.</p>
</header>

17
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/AccessDenied.cshtml.cs

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace EVABackend.Areas.Identity.Pages.Account
{
public class AccessDeniedModel : PageModel
{
public void OnGet()
{
}
}
}

12
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/ConfirmEmail.cshtml

@ -0,0 +1,12 @@
@page
@model ConfirmEmailModel
@{
ViewData["Title"] = "Confirm email";
}
<h2>@ViewData["Title"]</h2>
<div>
<p>
Thank you for confirming your email.
</p>
</div>

45
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using EVABackend.Areas.Identity.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace EVABackend.Areas.Identity.Pages.Account
{
[AllowAnonymous]
public class ConfirmEmailModel : PageModel
{
private readonly UserManager<EVABackendUser> _userManager;
public ConfirmEmailModel(UserManager<EVABackendUser> userManager)
{
_userManager = userManager;
}
public async Task<IActionResult> OnGetAsync(string userId, string code)
{
if (userId == null || code == null)
{
return RedirectToPage("/Index");
}
var user = await _userManager.FindByIdAsync(userId);
if (user == null)
{
return NotFound($"Unable to load user with ID '{userId}'.");
}
var result = await _userManager.ConfirmEmailAsync(user, code);
if (!result.Succeeded)
{
throw new InvalidOperationException($"Error confirming email for user with ID '{userId}':");
}
return Page();
}
}
}

34
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/ExternalLogin.cshtml

@ -0,0 +1,34 @@
@page
@model ExternalLoginModel
@{
ViewData["Title"] = "Register";
}
<h2>@ViewData["Title"]</h2>
<h4>Associate your @Model.LoginProvider account.</h4>
<hr />
<p class="text-info">
You've successfully authenticated with <strong>@Model.LoginProvider</strong>.
Please enter an email address for this site below and click the Register button to finish
logging in.
</p>
<div class="row">
<div class="col-md-4">
<form asp-page-handler="Confirmation" asp-route-returnUrl="@Model.ReturnUrl" method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Input.Email"></label>
<input asp-for="Input.Email" class="form-control" />
<span asp-validation-for="Input.Email" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-default">Register</button>
</form>
</div>
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
}

141
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs

@ -0,0 +1,141 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using EVABackend.Areas.Identity.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
namespace EVABackend.Areas.Identity.Pages.Account
{
[AllowAnonymous]
public class ExternalLoginModel : PageModel
{
private readonly SignInManager<EVABackendUser> _signInManager;
private readonly UserManager<EVABackendUser> _userManager;
private readonly ILogger<ExternalLoginModel> _logger;
public ExternalLoginModel(
SignInManager<EVABackendUser> signInManager,
UserManager<EVABackendUser> userManager,
ILogger<ExternalLoginModel> logger)
{
_signInManager = signInManager;
_userManager = userManager;
_logger = logger;
}
[BindProperty]
public InputModel Input { get; set; }
public string LoginProvider { get; set; }
public string ReturnUrl { get; set; }
[TempData]
public string ErrorMessage { get; set; }
public class InputModel
{
[Required]
[EmailAddress]
public string Email { get; set; }
}
public IActionResult OnGetAsync()
{
return RedirectToPage("./Login");
}
public IActionResult OnPost(string provider, string returnUrl = null)
{
// Request a redirect to the external login provider.
var redirectUrl = Url.Page("./ExternalLogin", pageHandler: "Callback", values: new { returnUrl });
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
return new ChallengeResult(provider, properties);
}
public async Task<IActionResult> OnGetCallbackAsync(string returnUrl = null, string remoteError = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
if (remoteError != null)
{
ErrorMessage = $"Error from external provider: {remoteError}";
return RedirectToPage("./Login", new {ReturnUrl = returnUrl });
}
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
ErrorMessage = "Error loading external login information.";
return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
}
// Sign in the user with this external login provider if the user already has a login.
var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor : true);
if (result.Succeeded)
{
_logger.LogInformation("{Name} logged in with {LoginProvider} provider.", info.Principal.Identity.Name, info.LoginProvider);
return LocalRedirect(returnUrl);
}
if (result.IsLockedOut)
{
return RedirectToPage("./Lockout");
}
else
{
// If the user does not have an account, then ask the user to create an account.
ReturnUrl = returnUrl;
LoginProvider = info.LoginProvider;
if (info.Principal.HasClaim(c => c.Type == ClaimTypes.Email))
{
Input = new InputModel
{
Email = info.Principal.FindFirstValue(ClaimTypes.Email)
};
}
return Page();
}
}
public async Task<IActionResult> OnPostConfirmationAsync(string returnUrl = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
// Get the information about the user from the external login provider
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
ErrorMessage = "Error loading external login information during confirmation.";
return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
}
if (ModelState.IsValid)
{
var user = new EVABackendUser { UserName = Input.Email, Email = Input.Email };
var result = await _userManager.CreateAsync(user);
if (result.Succeeded)
{
result = await _userManager.AddLoginAsync(user, info);
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation("User created an account using {Name} provider.", info.LoginProvider);
return LocalRedirect(returnUrl);
}
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
LoginProvider = info.LoginProvider;
ReturnUrl = returnUrl;
return Page();
}
}
}

26
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/ForgotPassword.cshtml

@ -0,0 +1,26 @@
@page
@model ForgotPasswordModel
@{
ViewData["Title"] = "Forgot your password?";
}
<h2>@ViewData["Title"]</h2>
<h4>Enter your email.</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Input.Email"></label>
<input asp-for="Input.Email" class="form-control" />
<span asp-validation-for="Input.Email" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
}

68
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs

@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using EVABackend.Areas.Identity.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace EVABackend.Areas.Identity.Pages.Account
{
[AllowAnonymous]
public class ForgotPasswordModel : PageModel
{
private readonly UserManager<EVABackendUser> _userManager;
private readonly IEmailSender _emailSender;
public ForgotPasswordModel(UserManager<EVABackendUser> userManager, IEmailSender emailSender)
{
_userManager = userManager;
_emailSender = emailSender;
}
[BindProperty]
public InputModel Input { get; set; }
public class InputModel
{
[Required]
[EmailAddress]
public string Email { get; set; }
}
public async Task<IActionResult> OnPostAsync()
{
if (ModelState.IsValid)
{
var user = await _userManager.FindByEmailAsync(Input.Email);
if (user == null || !(await _userManager.IsEmailConfirmedAsync(user)))
{
// Don't reveal that the user does not exist or is not confirmed
return RedirectToPage("./ForgotPasswordConfirmation");
}
// For more information on how to enable account confirmation and password reset please
// visit https://go.microsoft.com/fwlink/?LinkID=532713
var code = await _userManager.GeneratePasswordResetTokenAsync(user);
var callbackUrl = Url.Page(
"/Account/ResetPassword",
pageHandler: null,
values: new { code },
protocol: Request.Scheme);
await _emailSender.SendEmailAsync(
Input.Email,
"Reset Password",
$"Please reset your password by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
return RedirectToPage("./ForgotPasswordConfirmation");
}
return Page();
}
}
}

11
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml

@ -0,0 +1,11 @@
@page
@model ForgotPasswordConfirmation
@{
ViewData["Title"] = "Forgot password confirmation";
}
<h2>@ViewData["Title"]</h2>
<p>
Please check your email to reset your password.
</p>

16
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml.cs

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace EVABackend.Areas.Identity.Pages.Account
{
[AllowAnonymous]
public class ForgotPasswordConfirmation : PageModel
{
public void OnGet()
{
}
}
}

10
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Lockout.cshtml

@ -0,0 +1,10 @@
@page
@model LockoutModel
@{
ViewData["Title"] = "Locked out";
}
<header>
<h1 class="text-danger">@ViewData["Title"]</h1>
<p class="text-danger">This account has been locked out, please try again later.</p>
</header>

18
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Lockout.cshtml.cs

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace EVABackend.Areas.Identity.Pages.Account
{
[AllowAnonymous]
public class LockoutModel : PageModel
{
public void OnGet()
{
}
}
}

82
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Login.cshtml

@ -0,0 +1,82 @@
@page
@model LoginModel
@{
ViewData["Title"] = "Log in";
}
<h2>@ViewData["Title"]</h2>
<div class="row">
<div class="col-md-4">
<section>
<form method="post">
<h4>Use a local account to log in.</h4>
<hr />
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Input.Email"></label>
<input asp-for="Input.Email" class="form-control" />
<span asp-validation-for="Input.Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.Password"></label>
<input asp-for="Input.Password" class="form-control" />
<span asp-validation-for="Input.Password" class="text-danger"></span>
</div>
<div class="form-group">
<div class="checkbox">
<label asp-for="Input.RememberMe">
<input asp-for="Input.RememberMe" />
@Html.DisplayNameFor(m => m.Input.RememberMe)
</label>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-default">Log in</button>
</div>
<div class="form-group">
<p>
<a asp-page="./ForgotPassword">Forgot your password?</a>
</p>
<p>
<a asp-page="./Register" asp-route-returnUrl="@Model.ReturnUrl">Register as a new user</a>
</p>
</div>
</form>
</section>
</div>
<div class="col-md-6 col-md-offset-2">
<section>
<h4>Use another service to log in.</h4>
<hr />
@{
if ((Model.ExternalLogins?.Count ?? 0) == 0)
{
<div>
<p>
There are no external authentication services configured. See <a href="https://go.microsoft.com/fwlink/?LinkID=532715">this article</a>
for details on setting up this ASP.NET application to support logging in via external services.
</p>
</div>
}
else
{
<form asp-page="./ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" method="post" class="form-horizontal">
<div>
<p>
@foreach (var provider in Model.ExternalLogins)
{
<button type="submit" class="btn btn-default" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
}
</p>
</div>
</form>
}
}
</section>
</div>
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
}

103
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Login.cshtml.cs

@ -0,0 +1,103 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using EVABackend.Areas.Identity.Data;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
namespace EVABackend.Areas.Identity.Pages.Account
{
[AllowAnonymous]
public class LoginModel : PageModel
{
private readonly SignInManager<EVABackendUser> _signInManager;
private readonly ILogger<LoginModel> _logger;
public LoginModel(SignInManager<EVABackendUser> signInManager, ILogger<LoginModel> logger)
{
_signInManager = signInManager;
_logger = logger;
}
[BindProperty]
public InputModel Input { get; set; }
public IList<AuthenticationScheme> ExternalLogins { get; set; }
public string ReturnUrl { get; set; }
[TempData]
public string ErrorMessage { get; set; }
public class InputModel
{
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}
public async Task OnGetAsync(string returnUrl = null)
{
if (!string.IsNullOrEmpty(ErrorMessage))
{
ModelState.AddModelError(string.Empty, ErrorMessage);
}
returnUrl = returnUrl ?? Url.Content("~/");
// Clear the existing external cookie to ensure a clean login process
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
ReturnUrl = returnUrl;
}
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
if (ModelState.IsValid)
{
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: true);
if (result.Succeeded)
{
_logger.LogInformation("User logged in.");
return LocalRedirect(returnUrl);
}
if (result.RequiresTwoFactor)
{
return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe });
}
if (result.IsLockedOut)
{
_logger.LogWarning("User account locked out.");
return RedirectToPage("./Lockout");
}
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return Page();
}
}
// If we got this far, something failed, redisplay form
return Page();
}
}
}

41
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/LoginWith2fa.cshtml

@ -0,0 +1,41 @@
@page
@model LoginWith2faModel
@{
ViewData["Title"] = "Two-factor authentication";
}
<h2>@ViewData["Title"]</h2>
<hr />
<p>Your login is protected with an authenticator app. Enter your authenticator code below.</p>
<div class="row">
<div class="col-md-4">
<form method="post" asp-route-returnUrl="@Model.ReturnUrl">
<input asp-for="RememberMe" type="hidden" />
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Input.TwoFactorCode"></label>
<input asp-for="Input.TwoFactorCode" class="form-control" autocomplete="off" />
<span asp-validation-for="Input.TwoFactorCode" class="text-danger"></span>
</div>
<div class="form-group">
<div class="checkbox">
<label asp-for="Input.RememberMachine">
<input asp-for="Input.RememberMachine" />
@Html.DisplayNameFor(m => m.Input.RememberMachine)
</label>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-default">Log in</button>
</div>
</form>
</div>
</div>
<p>
Don't have access to your authenticator device? You can
<a asp-page="./LoginWithRecoveryCode" asp-route-returnUrl="@Model.ReturnUrl">log in with a recovery code</a>.
</p>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
}

99
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs

@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using EVABackend.Areas.Identity.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
namespace EVABackend.Areas.Identity.Pages.Account
{
[AllowAnonymous]
public class LoginWith2faModel : PageModel
{
private readonly SignInManager<EVABackendUser> _signInManager;
private readonly ILogger<LoginWith2faModel> _logger;
public LoginWith2faModel(SignInManager<EVABackendUser> signInManager, ILogger<LoginWith2faModel> logger)
{
_signInManager = signInManager;
_logger = logger;
}
[BindProperty]
public InputModel Input { get; set; }
public bool RememberMe { get; set; }
public string ReturnUrl { get; set; }
public class InputModel
{
[Required]
[StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
[DataType(DataType.Text)]
[Display(Name = "Authenticator code")]
public string TwoFactorCode { get; set; }
[Display(Name = "Remember this machine")]
public bool RememberMachine { get; set; }
}
public async Task<IActionResult> OnGetAsync(bool rememberMe, string returnUrl = null)
{
// Ensure the user has gone through the username & password screen first
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null)
{
throw new InvalidOperationException($"Unable to load two-factor authentication user.");
}
ReturnUrl = returnUrl;
RememberMe = rememberMe;
return Page();
}
public async Task<IActionResult> OnPostAsync(bool rememberMe, string returnUrl = null)
{
if (!ModelState.IsValid)
{
return Page();
}
returnUrl = returnUrl ?? Url.Content("~/");
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null)
{
throw new InvalidOperationException($"Unable to load two-factor authentication user.");
}
var authenticatorCode = Input.TwoFactorCode.Replace(" ", string.Empty).Replace("-", string.Empty);
var result = await _signInManager.TwoFactorAuthenticatorSignInAsync(authenticatorCode, rememberMe, Input.RememberMachine);
if (result.Succeeded)
{
_logger.LogInformation("User with ID '{UserId}' logged in with 2fa.", user.Id);
return LocalRedirect(returnUrl);
}
else if (result.IsLockedOut)
{
_logger.LogWarning("User with ID '{UserId}' account locked out.", user.Id);
return RedirectToPage("./Lockout");
}
else
{
_logger.LogWarning("Invalid authenticator code entered for user with ID '{UserId}'.", user.Id);
ModelState.AddModelError(string.Empty, "Invalid authenticator code.");
return Page();
}
}
}
}

29
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml

@ -0,0 +1,29 @@
@page
@model LoginWithRecoveryCodeModel
@{
ViewData["Title"] = "Recovery code verification";
}
<h2>@ViewData["Title"]</h2>
<hr />
<p>
You have requested to log in with a recovery code. This login will not be remembered until you provide
an authenticator app code at log in or disable 2FA and log in again.
</p>
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Input.RecoveryCode"></label>
<input asp-for="Input.RecoveryCode" class="form-control" autocomplete="off" />
<span asp-validation-for="Input.RecoveryCode" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-default">Log in</button>
</form>
</div>
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
}

90
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs

@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using EVABackend.Areas.Identity.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
namespace EVABackend.Areas.Identity.Pages.Account
{
[AllowAnonymous]
public class LoginWithRecoveryCodeModel : PageModel
{
private readonly SignInManager<EVABackendUser> _signInManager;
private readonly ILogger<LoginWithRecoveryCodeModel> _logger;
public LoginWithRecoveryCodeModel(SignInManager<EVABackendUser> signInManager, ILogger<LoginWithRecoveryCodeModel> logger)
{
_signInManager = signInManager;
_logger = logger;
}
[BindProperty]
public InputModel Input { get; set; }
public string ReturnUrl { get; set; }
public class InputModel
{
[BindProperty]
[Required]
[DataType(DataType.Text)]
[Display(Name = "Recovery Code")]
public string RecoveryCode { get; set; }
}
public async Task<IActionResult> OnGetAsync(string returnUrl = null)
{
// Ensure the user has gone through the username & password screen first
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null)
{
throw new InvalidOperationException($"Unable to load two-factor authentication user.");
}
ReturnUrl = returnUrl;
return Page();
}
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
if (!ModelState.IsValid)
{
return Page();
}
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null)
{
throw new InvalidOperationException($"Unable to load two-factor authentication user.");
}
var recoveryCode = Input.RecoveryCode.Replace(" ", string.Empty);
var result = await _signInManager.TwoFactorRecoveryCodeSignInAsync(recoveryCode);
if (result.Succeeded)
{
_logger.LogInformation("User with ID '{UserId}' logged in with a recovery code.", user.Id);
return LocalRedirect(returnUrl ?? Url.Content("~/"));
}
if (result.IsLockedOut)
{
_logger.LogWarning("User with ID '{UserId}' account locked out.", user.Id);
return RedirectToPage("./Lockout");
}
else
{
_logger.LogWarning("Invalid recovery code entered for user with ID '{UserId}' ", user.Id);
ModelState.AddModelError(string.Empty, "Invalid recovery code entered.");
return Page();
}
}
}
}

10
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Logout.cshtml

@ -0,0 +1,10 @@
@page
@model LogoutModel
@{
ViewData["Title"] = "Log out";
}
<header>
<h1>@ViewData["Title"]</h1>
<p>You have successfully logged out of the application.</p>
</header>

44
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Logout.cshtml.cs

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using EVABackend.Areas.Identity.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
namespace EVABackend.Areas.Identity.Pages.Account
{
[AllowAnonymous]
public class LogoutModel : PageModel
{
private readonly SignInManager<EVABackendUser> _signInManager;
private readonly ILogger<LogoutModel> _logger;
public LogoutModel(SignInManager<EVABackendUser> signInManager, ILogger<LogoutModel> logger)
{
_signInManager = signInManager;
_logger = logger;
}
public void OnGet()
{
}
public async Task<IActionResult> OnPost(string returnUrl = null)
{
await _signInManager.SignOutAsync();
_logger.LogInformation("User logged out.");
if (returnUrl != null)
{
return LocalRedirect(returnUrl);
}
else
{
return Page();
}
}
}
}

35
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml

@ -0,0 +1,35 @@
@page
@model ChangePasswordModel
@{
ViewData["Title"] = "Change password";
}
<h4>@ViewData["Title"]</h4>
@Html.Partial("_StatusMessage", Model.StatusMessage)
<div class="row">
<div class="col-md-6">
<form id="change-password-form" method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Input.OldPassword"></label>
<input asp-for="Input.OldPassword" class="form-control" />
<span asp-validation-for="Input.OldPassword" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.NewPassword"></label>
<input asp-for="Input.NewPassword" class="form-control" />
<span asp-validation-for="Input.NewPassword" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.ConfirmPassword"></label>
<input asp-for="Input.ConfirmPassword" class="form-control" />
<span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-default">Update password</button>
</form>
</div>
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
}

101
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs

@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using EVABackend.Areas.Identity.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
namespace EVABackend.Areas.Identity.Pages.Account.Manage
{
public class ChangePasswordModel : PageModel
{
private readonly UserManager<EVABackendUser> _userManager;
private readonly SignInManager<EVABackendUser> _signInManager;
private readonly ILogger<ChangePasswordModel> _logger;
public ChangePasswordModel(
UserManager<EVABackendUser> userManager,
SignInManager<EVABackendUser> signInManager,
ILogger<ChangePasswordModel> logger)
{
_userManager = userManager;
_signInManager = signInManager;
_logger = logger;
}
[BindProperty]
public InputModel Input { get; set; }
[TempData]
public string StatusMessage { get; set; }
public class InputModel
{
[Required]
[DataType(DataType.Password)]
[Display(Name = "Current password")]
public string OldPassword { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
public async Task<IActionResult> OnGetAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var hasPassword = await _userManager.HasPasswordAsync(user);
if (!hasPassword)
{
return RedirectToPage("./SetPassword");
}
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var changePasswordResult = await _userManager.ChangePasswordAsync(user, Input.OldPassword, Input.NewPassword);
if (!changePasswordResult.Succeeded)
{
foreach (var error in changePasswordResult.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
return Page();
}
await _signInManager.RefreshSignInAsync(user);
_logger.LogInformation("User changed their password successfully.");
StatusMessage = "Your password has been changed.";
return RedirectToPage();
}
}
}

34
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml

@ -0,0 +1,34 @@
@page
@model DeletePersonalDataModel
@{
ViewData["Title"] = "Delete Personal Data";
ViewData["ActivePage"] = ManageNavPages.DeletePersonalData;
}
<h4>@ViewData["Title"]</h4>
<div class="alert alert-warning" role="alert">
<p>
<span class="glyphicon glyphicon-warning-sign"></span>
<strong>Deleting this data will permanently remove your account, and this cannot be recovered.</strong>
</p>
</div>
<div>
<form id="delete-user" method="post" class="form-group">
<div asp-validation-summary="All" class="text-danger"></div>
@if (Model.RequirePassword)
{
<div class="form-group">
<label asp-for="Input.Password"></label>
<input asp-for="Input.Password" class="form-control" />
<span asp-validation-for="Input.Password" class="text-danger"></span>
</div>
}
<button class="btn btn-danger" type="submit">Delete data and close my account</button>
</form>
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
}

84
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs

@ -0,0 +1,84 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using EVABackend.Areas.Identity.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
namespace EVABackend.Areas.Identity.Pages.Account.Manage
{
public class DeletePersonalDataModel : PageModel
{
private readonly UserManager<EVABackendUser> _userManager;
private readonly SignInManager<EVABackendUser> _signInManager;
private readonly ILogger<DeletePersonalDataModel> _logger;
public DeletePersonalDataModel(
UserManager<EVABackendUser> userManager,
SignInManager<EVABackendUser> signInManager,
ILogger<DeletePersonalDataModel> logger)
{
_userManager = userManager;
_signInManager = signInManager;
_logger = logger;
}
[BindProperty]
public InputModel Input { get; set; }
public class InputModel
{
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
}
public bool RequirePassword { get; set; }
public async Task<IActionResult> OnGet()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
RequirePassword = await _userManager.HasPasswordAsync(user);
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
RequirePassword = await _userManager.HasPasswordAsync(user);
if (RequirePassword)
{
if (!await _userManager.CheckPasswordAsync(user, Input.Password))
{
ModelState.AddModelError(string.Empty, "Password not correct.");
return Page();
}
}
var result = await _userManager.DeleteAsync(user);
var userId = await _userManager.GetUserIdAsync(user);
if (!result.Succeeded)
{
throw new InvalidOperationException($"Unexpected error occurred deleteing user with ID '{userId}'.");
}
await _signInManager.SignOutAsync();
_logger.LogInformation("User with ID '{UserId}' deleted themselves.", userId);
return Redirect("~/");
}
}
}

26
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml

@ -0,0 +1,26 @@
@page
@model Disable2faModel
@{
ViewData["Title"] = "Disable two-factor authentication (2FA)";
ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication;
}
@Html.Partial("_StatusMessage", Model.StatusMessage)
<h2>@ViewData["Title"]</h2>
<div class="alert alert-warning" role="alert">
<p>
<span class="glyphicon glyphicon-warning-sign"></span>
<strong>This action only disables 2FA.</strong>
</p>
<p>
Disabling 2FA does not change the keys used in authenticator apps. If you wish to change the key
used in an authenticator app you should <a asp-page="./ResetAuthenticator">reset your authenticator keys.</a>
</p>
</div>
<div>
<form method="post" class="form-group">
<button class="btn btn-danger" type="submit">Disable 2FA</button>
</form>
</div>

64
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs

@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EVABackend.Areas.Identity.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
namespace EVABackend.Areas.Identity.Pages.Account.Manage
{
public class Disable2faModel : PageModel
{
private readonly UserManager<EVABackendUser> _userManager;
private readonly ILogger<Disable2faModel> _logger;
public Disable2faModel(
UserManager<EVABackendUser> userManager,
ILogger<Disable2faModel> logger)
{
_userManager = userManager;
_logger = logger;
}
[TempData]
public string StatusMessage { get; set; }
public async Task<IActionResult> OnGet()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
if (!await _userManager.GetTwoFactorEnabledAsync(user))
{
throw new InvalidOperationException($"Cannot disable 2FA for user with ID '{_userManager.GetUserId(User)}' as it's not currently enabled.");
}
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var disable2faResult = await _userManager.SetTwoFactorEnabledAsync(user, false);
if (!disable2faResult.Succeeded)
{
throw new InvalidOperationException($"Unexpected error occurred disabling 2FA for user with ID '{_userManager.GetUserId(User)}'.");
}
_logger.LogInformation("User with ID '{UserId}' has disabled 2fa.", _userManager.GetUserId(User));
StatusMessage = "2fa has been disabled. You can reenable 2fa when you setup an authenticator app";
return RedirectToPage("./TwoFactorAuthentication");
}
}
}

12
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml

@ -0,0 +1,12 @@
@page
@model DownloadPersonalDataModel
@{
ViewData["Title"] = "Download Your Data";
ViewData["ActivePage"] = ManageNavPages.DownloadPersonalData;
}
<h4>@ViewData["Title"]</h4>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
}

51
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EVABackend.Areas.Identity.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
namespace EVABackend.Areas.Identity.Pages.Account.Manage
{
public class DownloadPersonalDataModel : PageModel
{
private readonly UserManager<EVABackendUser> _userManager;
private readonly ILogger<DownloadPersonalDataModel> _logger;
public DownloadPersonalDataModel(
UserManager<EVABackendUser> userManager,
ILogger<DownloadPersonalDataModel> logger)
{
_userManager = userManager;
_logger = logger;
}
public async Task<IActionResult> OnPostAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
_logger.LogInformation("User with ID '{UserId}' asked for their personal data.", _userManager.GetUserId(User));
// Only include personal data for download
var personalData = new Dictionary<string, string>();
var personalDataProps = typeof(EVABackendUser).GetProperties().Where(
prop => Attribute.IsDefined(prop, typeof(PersonalDataAttribute)));
foreach (var p in personalDataProps)
{
personalData.Add(p.Name, p.GetValue(user)?.ToString() ?? "null");
}
Response.Headers.Add("Content-Disposition", "attachment; filename=PersonalData.json");
return new FileContentResult(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(personalData)), "text/json");
}
}
}

54
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml

@ -0,0 +1,54 @@
@page
@model EnableAuthenticatorModel
@{
ViewData["Title"] = "Configure authenticator app";
ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication;
}
@Html.Partial("_StatusMessage", Model.StatusMessage)
<h4>@ViewData["Title"]</h4>
<div>
<p>To use an authenticator app go through the following steps:</p>
<ol class="list">
<li>
<p>
Download a two-factor authenticator app like Microsoft Authenticator for
<a href="https://go.microsoft.com/fwlink/?Linkid=825071">Windows Phone</a>,
<a href="https://go.microsoft.com/fwlink/?Linkid=825072">Android</a> and
<a href="https://go.microsoft.com/fwlink/?Linkid=825073">iOS</a> or
Google Authenticator for
<a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&amp;hl=en">Android</a> and
<a href="https://itunes.apple.com/us/app/google-authenticator/id388497605?mt=8">iOS</a>.
</p>
</li>
<li>
<p>Scan the QR Code or enter this key <kbd>@Model.SharedKey</kbd> into your two factor authenticator app. Spaces and casing do not matter.</p>
<div class="alert alert-info">To enable QR code generation please read our <a href="https://go.microsoft.com/fwlink/?Linkid=852423">documentation</a>.</div>
<div id="qrCode"></div>
<div id="qrCodeData" data-url="@Html.Raw(Model.AuthenticatorUri)"></div>
</li>
<li>
<p>
Once you have scanned the QR code or input the key above, your two factor authentication app will provide you
with a unique code. Enter the code in the confirmation box below.
</p>
<div class="row">
<div class="col-md-6">
<form method="post">
<div class="form-group">
<label asp-for="Input.Code" class="control-label">Verification Code</label>
<input asp-for="Input.Code" class="form-control" autocomplete="off" />
<span asp-validation-for="Input.Code" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-default">Verify</button>
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
</form>
</div>
</div>
</li>
</ol>
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
}

157
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs

@ -0,0 +1,157 @@
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System.Text;
using System.Text.Encodings.Web;
using System.Linq;
using System.Threading.Tasks;
using EVABackend.Areas.Identity.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
namespace EVABackend.Areas.Identity.Pages.Account.Manage
{
public class EnableAuthenticatorModel : PageModel
{
private readonly UserManager<EVABackendUser> _userManager;
private readonly ILogger<EnableAuthenticatorModel> _logger;
private readonly UrlEncoder _urlEncoder;
private const string AuthenticatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6";
public EnableAuthenticatorModel(
UserManager<EVABackendUser> userManager,
ILogger<EnableAuthenticatorModel> logger,
UrlEncoder urlEncoder)
{
_userManager = userManager;
_logger = logger;
_urlEncoder = urlEncoder;
}
public string SharedKey { get; set; }
public string AuthenticatorUri { get; set; }
[TempData]
public string[] RecoveryCodes { get; set; }
[TempData]
public string StatusMessage { get; set; }
[BindProperty]
public InputModel Input { get; set; }
public class InputModel
{
[Required]
[StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
[DataType(DataType.Text)]
[Display(Name = "Verification Code")]
public string Code { get; set; }
}
public async Task<IActionResult> OnGetAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
await LoadSharedKeyAndQrCodeUriAsync(user);
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
if (!ModelState.IsValid)
{
await LoadSharedKeyAndQrCodeUriAsync(user);
return Page();
}
// Strip spaces and hypens
var verificationCode = Input.Code.Replace(" ", string.Empty).Replace("-", string.Empty);
var is2faTokenValid = await _userManager.VerifyTwoFactorTokenAsync(
user, _userManager.Options.Tokens.AuthenticatorTokenProvider, verificationCode);
if (!is2faTokenValid)
{
ModelState.AddModelError("Input.Code", "Verification code is invalid.");
await LoadSharedKeyAndQrCodeUriAsync(user);
return Page();
}
await _userManager.SetTwoFactorEnabledAsync(user, true);
var userId = await _userManager.GetUserIdAsync(user);
_logger.LogInformation("User with ID '{UserId}' has enabled 2FA with an authenticator app.", userId);
StatusMessage = "Your authenticator app has been verified.";
if (await _userManager.CountRecoveryCodesAsync(user) == 0)
{
var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10);
RecoveryCodes = recoveryCodes.ToArray();
return RedirectToPage("./ShowRecoveryCodes");
}
else
{
return RedirectToPage("./TwoFactorAuthentication");
}
}
private async Task LoadSharedKeyAndQrCodeUriAsync(EVABackendUser user)
{
// Load the authenticator key & QR code URI to display on the form
var unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);
if (string.IsNullOrEmpty(unformattedKey))
{
await _userManager.ResetAuthenticatorKeyAsync(user);
unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);
}
SharedKey = FormatKey(unformattedKey);
var email = await _userManager.GetEmailAsync(user);
AuthenticatorUri = GenerateQrCodeUri(email, unformattedKey);
}
private string FormatKey(string unformattedKey)
{
var result = new StringBuilder();
int currentPosition = 0;
while (currentPosition + 4 < unformattedKey.Length)
{
result.Append(unformattedKey.Substring(currentPosition, 4)).Append(" ");
currentPosition += 4;
}
if (currentPosition < unformattedKey.Length)
{
result.Append(unformattedKey.Substring(currentPosition));
}
return result.ToString().ToLowerInvariant();
}
private string GenerateQrCodeUri(string email, string unformattedKey)
{
return string.Format(
AuthenticatorUriFormat,
_urlEncoder.Encode("EVABackend"),
_urlEncoder.Encode(email),
unformattedKey);
}
}
}

52
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml

@ -0,0 +1,52 @@
@page
@model ExternalLoginsModel
@{
ViewData["Title"] = "Manage your external logins";
}
@Html.Partial("_StatusMessage", Model.StatusMessage)
@if (Model.CurrentLogins?.Count > 0)
{
<h4>Registered Logins</h4>
<table class="table">
<tbody>
@foreach (var login in Model.CurrentLogins)
{
<tr>
<td>@login.LoginProvider</td>
<td>
@if (Model.ShowRemoveButton)
{
<form id="remove-login" asp-page-handler="RemoveLogin" method="post">
<div>
<input asp-for="@login.LoginProvider" name="LoginProvider" type="hidden" />
<input asp-for="@login.ProviderKey" name="ProviderKey" type="hidden" />
<button type="submit" class="btn btn-default" title="Remove this @login.LoginProvider login from your account">Remove</button>
</div>
</form>
}
else
{
@: &nbsp;
}
</td>
</tr>
}
</tbody>
</table>
}
@if (Model.OtherLogins?.Count > 0)
{
<h4>Add another service to log in.</h4>
<hr />
<form id="link-login-form" asp-page-handler="LinkLogin" method="post" class="form-horizontal">
<div id="socialLoginList">
<p>
@foreach (var provider in Model.OtherLogins)
{
<button id="link-login-button" type="submit" class="btn btn-default" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
}
</p>
</div>
</form>
}

109
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs

@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EVABackend.Areas.Identity.Data;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace EVABackend.Areas.Identity.Pages.Account.Manage
{
public class ExternalLoginsModel : PageModel
{
private readonly UserManager<EVABackendUser> _userManager;
private readonly SignInManager<EVABackendUser> _signInManager;
public ExternalLoginsModel(
UserManager<EVABackendUser> userManager,
SignInManager<EVABackendUser> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}
public IList<UserLoginInfo> CurrentLogins { get; set; }
public IList<AuthenticationScheme> OtherLogins { get; set; }
public bool ShowRemoveButton { get; set; }
[TempData]
public string StatusMessage { get; set; }
public async Task<IActionResult> OnGetAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
CurrentLogins = await _userManager.GetLoginsAsync(user);
OtherLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync())
.Where(auth => CurrentLogins.All(ul => auth.Name != ul.LoginProvider))
.ToList();
ShowRemoveButton = user.PasswordHash != null || CurrentLogins.Count > 1;
return Page();
}
public async Task<IActionResult> OnPostRemoveLoginAsync(string loginProvider, string providerKey)
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var result = await _userManager.RemoveLoginAsync(user, loginProvider, providerKey);
if (!result.Succeeded)
{
var userId = await _userManager.GetUserIdAsync(user);
throw new InvalidOperationException($"Unexpected error occurred removing external login for user with ID '{userId}'.");
}
await _signInManager.RefreshSignInAsync(user);
StatusMessage = "The external login was removed.";
return RedirectToPage();
}
public async Task<IActionResult> OnPostLinkLoginAsync(string provider)
{
// Clear the existing external cookie to ensure a clean login process
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
// Request a redirect to the external login provider to link a login for the current user
var redirectUrl = Url.Page("./ExternalLogins", pageHandler: "LinkLoginCallback");
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, _userManager.GetUserId(User));
return new ChallengeResult(provider, properties);
}
public async Task<IActionResult> OnGetLinkLoginCallbackAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var info = await _signInManager.GetExternalLoginInfoAsync(await _userManager.GetUserIdAsync(user));
if (info == null)
{
throw new InvalidOperationException($"Unexpected error occurred loading external login info for user with ID '{user.Id}'.");
}
var result = await _userManager.AddLoginAsync(user, info);
if (!result.Succeeded)
{
throw new InvalidOperationException($"Unexpected error occurred adding external login for user with ID '{user.Id}'.");
}
// Clear the existing external cookie to ensure a clean login process
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
StatusMessage = "The external login was added.";
return RedirectToPage();
}
}
}

27
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml

@ -0,0 +1,27 @@
@page
@model GenerateRecoveryCodesModel
@{
ViewData["Title"] = "Generate two-factor authentication (2FA) recovery codes";
ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication;
}
@Html.Partial("_StatusMessage", Model.StatusMessage)
<h4>@ViewData["Title"]</h4>
<div class="alert alert-warning" role="alert">
<p>
<span class="glyphicon glyphicon-warning-sign"></span>
<strong>Put these codes in a safe place.</strong>
</p>
<p>
If you lose your device and don't have the recovery codes you will lose access to your account.
</p>
<p>
Generating new recovery codes does not change the keys used in authenticator apps. If you wish to change the key
used in an authenticator app you should <a asp-page="./ResetAuthenticator">reset your authenticator keys.</a>
</p>
</div>
<div>
<form method="post" class="form-group">
<button class="btn btn-danger" type="submit">Generate Recovery Codes</button>
</form>
</div>

73
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs

@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EVABackend.Areas.Identity.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
namespace EVABackend.Areas.Identity.Pages.Account.Manage
{
public class GenerateRecoveryCodesModel : PageModel
{
private readonly UserManager<EVABackendUser> _userManager;
private readonly ILogger<GenerateRecoveryCodesModel> _logger;
public GenerateRecoveryCodesModel(
UserManager<EVABackendUser> userManager,
ILogger<GenerateRecoveryCodesModel> logger)
{
_userManager = userManager;
_logger = logger;
}
[TempData]
public string[] RecoveryCodes { get; set; }
[TempData]
public string StatusMessage { get; set; }
public async Task<IActionResult> OnGetAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var isTwoFactorEnabled = await _userManager.GetTwoFactorEnabledAsync(user);
if (!isTwoFactorEnabled)
{
var userId = await _userManager.GetUserIdAsync(user);
throw new InvalidOperationException($"Cannot generate recovery codes for user with ID '{userId}' because they do not have 2FA enabled.");
}
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var isTwoFactorEnabled = await _userManager.GetTwoFactorEnabledAsync(user);
var userId = await _userManager.GetUserIdAsync(user);
if (!isTwoFactorEnabled)
{
throw new InvalidOperationException($"Cannot generate recovery codes for user with ID '{userId}' as they do not have 2FA enabled.");
}
var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10);
RecoveryCodes = recoveryCodes.ToArray();
_logger.LogInformation("User with ID '{UserId}' has generated new 2FA recovery codes.", userId);
StatusMessage = "You have generated new recovery codes.";
return RedirectToPage("./ShowRecoveryCodes");
}
}
}

45
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/Index.cshtml

@ -0,0 +1,45 @@
@page
@model IndexModel
@{
ViewData["Title"] = "Profile";
}
<h4>@ViewData["Title"]</h4>
@Html.Partial("_StatusMessage", Model.StatusMessage)
<div class="row">
<div class="col-md-6">
<form id="profile-form" method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Username"></label>
<input asp-for="Username" class="form-control" disabled />
</div>
<div class="form-group">
<label asp-for="Input.Email"></label>
@if (Model.IsEmailConfirmed)
{
<div class="input-group">
<input asp-for="Input.Email" class="form-control" />
<span class="input-group-addon" aria-hidden="true"><span class="glyphicon glyphicon-ok text-success"></span></span>
</div>
}
else
{
<input asp-for="Input.Email" class="form-control" />
<button id="email-verification" type="submit" asp-page-handler="SendVerificationEmail" class="btn btn-link">Send verification email</button>
}
<span asp-validation-for="Input.Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.PhoneNumber"></label>
<input asp-for="Input.PhoneNumber" class="form-control" />
<span asp-validation-for="Input.PhoneNumber" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-default">Save</button>
</form>
</div>
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
}

148
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs

@ -0,0 +1,148 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using EVABackend.Areas.Identity.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace EVABackend.Areas.Identity.Pages.Account.Manage
{
public partial class IndexModel : PageModel
{
private readonly UserManager<EVABackendUser> _userManager;
private readonly SignInManager<EVABackendUser> _signInManager;
private readonly IEmailSender _emailSender;
public IndexModel(
UserManager<EVABackendUser> userManager,
SignInManager<EVABackendUser> signInManager,
IEmailSender emailSender)
{
_userManager = userManager;
_signInManager = signInManager;
_emailSender = emailSender;
}
public string Username { get; set; }
public bool IsEmailConfirmed { get; set; }
[TempData]
public string StatusMessage { get; set; }
[BindProperty]
public InputModel Input { get; set; }
public class InputModel
{
[Required]
[EmailAddress]
public string Email { get; set; }
[Phone]
[Display(Name = "Phone number")]
public string PhoneNumber { get; set; }
}
public async Task<IActionResult> OnGetAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var userName = await _userManager.GetUserNameAsync(user);
var email = await _userManager.GetEmailAsync(user);
var phoneNumber = await _userManager.GetPhoneNumberAsync(user);
Username = userName;
Input = new InputModel
{
Email = email,
PhoneNumber = phoneNumber
};
IsEmailConfirmed = await _userManager.IsEmailConfirmedAsync(user);
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var email = await _userManager.GetEmailAsync(user);
if (Input.Email != email)
{
var setEmailResult = await _userManager.SetEmailAsync(user, Input.Email);
if (!setEmailResult.Succeeded)
{
var userId = await _userManager.GetUserIdAsync(user);
throw new InvalidOperationException($"Unexpected error occurred setting email for user with ID '{userId}'.");
}
}
var phoneNumber = await _userManager.GetPhoneNumberAsync(user);
if (Input.PhoneNumber != phoneNumber)
{
var setPhoneResult = await _userManager.SetPhoneNumberAsync(user, Input.PhoneNumber);
if (!setPhoneResult.Succeeded)
{
var userId = await _userManager.GetUserIdAsync(user);
throw new InvalidOperationException($"Unexpected error occurred setting phone number for user with ID '{userId}'.");
}
}
await _signInManager.RefreshSignInAsync(user);
StatusMessage = "Your profile has been updated";
return RedirectToPage();
}
public async Task<IActionResult> OnPostSendVerificationEmailAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var userId = await _userManager.GetUserIdAsync(user);
var email = await _userManager.GetEmailAsync(user);
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { userId = userId, code = code },
protocol: Request.Scheme);
await _emailSender.SendEmailAsync(
email,
"Confirm your email",
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
StatusMessage = "Verification email sent. Please check your email.";
return RedirectToPage();
}
}
}

46
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace EVABackend.Areas.Identity.Pages.Account.Manage
{
public static class ManageNavPages
{
public static string Index => "Index";
public static string ChangePassword => "ChangePassword";
public static string DownloadPersonalData => "DownloadPersonalData";
public static string DeletePersonalData => "DeletePersonalData";
public static string ExternalLogins => "ExternalLogins";
public static string PersonalData => "PersonalData";
public static string TwoFactorAuthentication => "TwoFactorAuthentication";
public static string IndexNavClass(ViewContext viewContext) => PageNavClass(viewContext, Index);
public static string ChangePasswordNavClass(ViewContext viewContext) => PageNavClass(viewContext, ChangePassword);
public static string DownloadPersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, DownloadPersonalData);
public static string DeletePersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, DeletePersonalData);
public static string ExternalLoginsNavClass(ViewContext viewContext) => PageNavClass(viewContext, ExternalLogins);
public static string PersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, PersonalData);
public static string TwoFactorAuthenticationNavClass(ViewContext viewContext) => PageNavClass(viewContext, TwoFactorAuthentication);
public static string PageNavClass(ViewContext viewContext, string page)
{
var activePage = viewContext.ViewData["ActivePage"] as string
?? System.IO.Path.GetFileNameWithoutExtension(viewContext.ActionDescriptor.DisplayName);
return string.Equals(activePage, page, StringComparison.OrdinalIgnoreCase) ? "active" : null;
}
}
}

27
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml

@ -0,0 +1,27 @@
@page
@model PersonalDataModel
@{
ViewData["Title"] = "Personal Data";
ViewData["ActivePage"] = ManageNavPages.PersonalData;
}
<h4>@ViewData["Title"]</h4>
<div class="row">
<div class="col-md-6">
<p>Your account contains personal data that you have given us. This page allows you to download or delete that data.</p>
<p>
<strong>Deleting this data will permanently remove your account, and this cannot be recovered.</strong>
</p>
<form asp-page="DownloadPersonalData" method="post" class="form-group">
<button class="btn btn-default" type="submit">Download</button>
</form>
<p>
<a asp-page="DeletePersonalData" class="btn btn-default">Delete</a>
</p>
</div>
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
}

34
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs

@ -0,0 +1,34 @@
using System.Threading.Tasks;
using EVABackend.Areas.Identity.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
namespace EVABackend.Areas.Identity.Pages.Account.Manage
{
public class PersonalDataModel : PageModel
{
private readonly UserManager<EVABackendUser> _userManager;
private readonly ILogger<PersonalDataModel> _logger;
public PersonalDataModel(
UserManager<EVABackendUser> userManager,
ILogger<PersonalDataModel> logger)
{
_userManager = userManager;
_logger = logger;
}
public async Task<IActionResult> OnGet()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
return Page();
}
}
}

24
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml

@ -0,0 +1,24 @@
@page
@model ResetAuthenticatorModel
@{
ViewData["Title"] = "Reset authenticator key";
ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication;
}
@Html.Partial("_StatusMessage", Model.StatusMessage)
<h4>@ViewData["Title"]</h4>
<div class="alert alert-warning" role="alert">
<p>
<span class="glyphicon glyphicon-warning-sign"></span>
<strong>If you reset your authenticator key your authenticator app will not work until you reconfigure it.</strong>
</p>
<p>
This process disables 2FA until you verify your authenticator app.
If you do not complete your authenticator app configuration you may lose access to your account.
</p>
</div>
<div>
<form id="reset-authenticator-form" method="post" class="form-group">
<button id="reset-authenticator-button" class="btn btn-danger" type="submit">Reset authenticator key</button>
</form>
</div>

61
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml.cs

@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EVABackend.Areas.Identity.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
namespace EVABackend.Areas.Identity.Pages.Account.Manage
{
public class ResetAuthenticatorModel : PageModel
{
UserManager<EVABackendUser> _userManager;
private readonly SignInManager<EVABackendUser> _signInManager;
ILogger<ResetAuthenticatorModel> _logger;
public ResetAuthenticatorModel(
UserManager<EVABackendUser> userManager,
SignInManager<EVABackendUser> signInManager,
ILogger<ResetAuthenticatorModel> logger)
{
_userManager = userManager;
_signInManager = signInManager;
_logger = logger;
}
[TempData]
public string StatusMessage { get; set; }
public async Task<IActionResult> OnGet()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
await _userManager.SetTwoFactorEnabledAsync(user, false);
await _userManager.ResetAuthenticatorKeyAsync(user);
_logger.LogInformation("User with ID '{UserId}' has reset their authentication app key.", user.Id);
await _signInManager.RefreshSignInAsync(user);
StatusMessage = "Your authenticator app key has been reset, you will need to configure your authenticator app using the new key.";
return RedirectToPage("./EnableAuthenticator");
}
}
}

35
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml

@ -0,0 +1,35 @@
@page
@model SetPasswordModel
@{
ViewData["Title"] = "Set password";
ViewData["ActivePage"] = ManageNavPages.ChangePassword;
}
<h4>Set your password</h4>
@Html.Partial("_StatusMessage", Model.StatusMessage)
<p class="text-info">
You do not have a local username/password for this site. Add a local
account so you can log in without an external login.
</p>
<div class="row">
<div class="col-md-6">
<form id="set-password-form" method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Input.NewPassword"></label>
<input asp-for="Input.NewPassword" class="form-control" />
<span asp-validation-for="Input.NewPassword" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.ConfirmPassword"></label>
<input asp-for="Input.ConfirmPassword" class="form-control" />
<span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-default">Set password</button>
</form>
</div>
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
}

93
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs

@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using EVABackend.Areas.Identity.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace EVABackend.Areas.Identity.Pages.Account.Manage
{
public class SetPasswordModel : PageModel
{
private readonly UserManager<EVABackendUser> _userManager;
private readonly SignInManager<EVABackendUser> _signInManager;
public SetPasswordModel(
UserManager<EVABackendUser> userManager,
SignInManager<EVABackendUser> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}
[BindProperty]
public InputModel Input { get; set; }
[TempData]
public string StatusMessage { get; set; }
public class InputModel
{
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
public async Task<IActionResult> OnGetAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var hasPassword = await _userManager.HasPasswordAsync(user);
if (hasPassword)
{
return RedirectToPage("./ChangePassword");
}
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var addPasswordResult = await _userManager.AddPasswordAsync(user, Input.NewPassword);
if (!addPasswordResult.Succeeded)
{
foreach (var error in addPasswordResult.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
return Page();
}
await _signInManager.RefreshSignInAsync(user);
StatusMessage = "Your password has been set.";
return RedirectToPage();
}
}
}

56
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml

@ -0,0 +1,56 @@
@page
@model TwoFactorAuthenticationModel
@{
ViewData["Title"] = "Two-factor authentication (2FA)";
}
@Html.Partial("_StatusMessage", Model.StatusMessage)
<h4>@ViewData["Title"]</h4>
@if (Model.Is2faEnabled)
{
if (Model.RecoveryCodesLeft == 0)
{
<div class="alert alert-danger">
<strong>You have no recovery codes left.</strong>
<p>You must <a asp-page="./GenerateRecoveryCodes">generate a new set of recovery codes</a> before you can log in with a recovery code.</p>
</div>
}
else if (Model.RecoveryCodesLeft == 1)
{
<div class="alert alert-danger">
<strong>You have 1 recovery code left.</strong>
<p>You can <a asp-page="./GenerateRecoveryCodes">generate a new set of recovery codes</a>.</p>
</div>
}
else if (Model.RecoveryCodesLeft <= 3)
{
<div class="alert alert-warning">
<strong>You have @Model.RecoveryCodesLeft recovery codes left.</strong>
<p>You should <a asp-page="./GenerateRecoveryCodes">generate a new set of recovery codes</a>.</p>
</div>
}
if (Model.IsMachineRemembered)
{
<form method="post" style="display: inline-block">
<button type="submit" class="btn btn-default">Forget this browser</button>
</form>
}
<a asp-page="./Disable2fa" class="btn btn-default">Disable 2FA</a>
<a asp-page="./GenerateRecoveryCodes" class="btn btn-default">Reset recovery codes</a>
}
<h5>Authenticator app</h5>
@if (!Model.HasAuthenticator)
{
<a id="enable-authenticator" asp-page="./EnableAuthenticator" class="btn btn-default">Add authenticator app</a>
}
else
{
<a id="enable-authenticator" asp-page="./EnableAuthenticator" class="btn btn-default">Setup authenticator app</a>
<a id="reset-authenticator" asp-page="./ResetAuthenticator" class="btn btn-default">Reset authenticator app</a>
}
@section Scripts {
<partial name="_ValidationScriptsPartial" />
}

72
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs

@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EVABackend.Areas.Identity.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
namespace EVABackend.Areas.Identity.Pages.Account.Manage
{
public class TwoFactorAuthenticationModel : PageModel
{
private const string AuthenicatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}";
private readonly UserManager<EVABackendUser> _userManager;
private readonly SignInManager<EVABackendUser> _signInManager;
private readonly ILogger<TwoFactorAuthenticationModel> _logger;
public TwoFactorAuthenticationModel(
UserManager<EVABackendUser> userManager,
SignInManager<EVABackendUser> signInManager,
ILogger<TwoFactorAuthenticationModel> logger)
{
_userManager = userManager;
_signInManager = signInManager;
_logger = logger;
}
public bool HasAuthenticator { get; set; }
public int RecoveryCodesLeft { get; set; }
[BindProperty]
public bool Is2faEnabled { get; set; }
public bool IsMachineRemembered { get; set; }
[TempData]
public string StatusMessage { get; set; }
public async Task<IActionResult> OnGet()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
HasAuthenticator = await _userManager.GetAuthenticatorKeyAsync(user) != null;
Is2faEnabled = await _userManager.GetTwoFactorEnabledAsync(user);
IsMachineRemembered = await _signInManager.IsTwoFactorClientRememberedAsync(user);
RecoveryCodesLeft = await _userManager.CountRecoveryCodesAsync(user);
return Page();
}
public async Task<IActionResult> OnPost()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
await _signInManager.ForgetTwoFactorClientAsync();
StatusMessage = "The current browser has been forgotten. When you login again from this browser you will be prompted for your 2fa code.";
return RedirectToPage();
}
}
}

22
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/_Layout.cshtml

@ -0,0 +1,22 @@
@{
Layout = "/Areas/Identity/Pages/_Layout.cshtml";
}
<h2>Manage your account</h2>
<div>
<h4>Change your account settings</h4>
<hr />
<div class="row">
<div class="col-md-3">
<partial name="_ManageNav" />
</div>
<div class="col-md-9">
@RenderBody()
</div>
</div>
</div>
@section Scripts {
@RenderSection("Scripts", required: false)
}

14
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml

@ -0,0 +1,14 @@
@inject SignInManager<EVABackendUser> SignInManager
@{
var hasExternalLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync()).Any();
}
<ul class="nav nav-pills nav-stacked">
<li class="@ManageNavPages.IndexNavClass(ViewContext)"><a asp-page="./Index">Profile</a></li>
<li class="@ManageNavPages.ChangePasswordNavClass(ViewContext)"><a id="change-password" asp-page="./ChangePassword">Password</a></li>
@if (hasExternalLogins)
{
<li class="@ManageNavPages.ExternalLoginsNavClass(ViewContext)"><a id="external-login" asp-page="./ExternalLogins">External logins</a></li>
}
<li class="@ManageNavPages.TwoFactorAuthenticationNavClass(ViewContext)"><a asp-page="./TwoFactorAuthentication">Two-factor authentication</a></li>
<li class="@ManageNavPages.PersonalDataNavClass(ViewContext)"><a asp-page="./PersonalData">Personal data</a></li>
</ul>

10
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml

@ -0,0 +1,10 @@
@model string
@if (!String.IsNullOrEmpty(Model))
{
var statusMessageClass = Model.StartsWith("Error") ? "danger" : "success";
<div class="alert alert-@statusMessageClass alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
@Model
</div>
}

1
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml

@ -0,0 +1 @@
@using EVABackend.Areas.Identity.Pages.Account.Manage

37
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Register.cshtml

@ -0,0 +1,37 @@
@page
@model RegisterModel
@{
ViewData["Title"] = "Register";
}
<h2>@ViewData["Title"]</h2>
<div class="row">
<div class="col-md-4">
<form asp-route-returnUrl="@Model.ReturnUrl" method="post">
<h4>Create a new account.</h4>
<hr />
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Input.Email"></label>
<input asp-for="Input.Email" class="form-control" />
<span asp-validation-for="Input.Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.Password"></label>
<input asp-for="Input.Password" class="form-control" />
<span asp-validation-for="Input.Password" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.ConfirmPassword"></label>
<input asp-for="Input.ConfirmPassword" class="form-control" />
<span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-default">Register</button>
</form>
</div>
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
}

99
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/Register.cshtml.cs

@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using EVABackend.Areas.Identity.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
namespace EVABackend.Areas.Identity.Pages.Account
{
[AllowAnonymous]
public class RegisterModel : PageModel
{
private readonly SignInManager<EVABackendUser> _signInManager;
private readonly UserManager<EVABackendUser> _userManager;
private readonly ILogger<RegisterModel> _logger;
private readonly IEmailSender _emailSender;
public RegisterModel(
UserManager<EVABackendUser> userManager,
SignInManager<EVABackendUser> signInManager,
ILogger<RegisterModel> logger,
IEmailSender emailSender)
{
_userManager = userManager;
_signInManager = signInManager;
_logger = logger;
_emailSender = emailSender;
}
[BindProperty]
public InputModel Input { get; set; }
public string ReturnUrl { get; set; }
public class InputModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
public void OnGet(string returnUrl = null)
{
ReturnUrl = returnUrl;
}
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
if (ModelState.IsValid)
{
var user = new EVABackendUser { UserName = Input.Email, Email = Input.Email };
var result = await _userManager.CreateAsync(user, Input.Password);
if (result.Succeeded)
{
_logger.LogInformation("User created a new account with password.");
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { userId = user.Id, code = code },
protocol: Request.Scheme);
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
await _signInManager.SignInAsync(user, isPersistent: false);
return LocalRedirect(returnUrl);
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
// If we got this far, something failed, redisplay form
return Page();
}
}
}

37
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/ResetPassword.cshtml

@ -0,0 +1,37 @@
@page
@model ResetPasswordModel
@{
ViewData["Title"] = "Reset password";
}
<h2>@ViewData["Title"]</h2>
<h4>Reset your password.</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<input asp-for="Input.Code" type="hidden" />
<div class="form-group">
<label asp-for="Input.Email"></label>
<input asp-for="Input.Email" class="form-control" />
<span asp-validation-for="Input.Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.Password"></label>
<input asp-for="Input.Password" class="form-control" />
<span asp-validation-for="Input.Password" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.ConfirmPassword"></label>
<input asp-for="Input.ConfirmPassword" class="form-control" />
<span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-default">Reset</button>
</form>
</div>
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
}

89
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs

@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using EVABackend.Areas.Identity.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace EVABackend.Areas.Identity.Pages.Account
{
[AllowAnonymous]
public class ResetPasswordModel : PageModel
{
private readonly UserManager<EVABackendUser> _userManager;
public ResetPasswordModel(UserManager<EVABackendUser> userManager)
{
_userManager = userManager;
}
[BindProperty]
public InputModel Input { get; set; }
public class InputModel
{
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
public string Code { get; set; }
}
public IActionResult OnGet(string code = null)
{
if (code == null)
{
return BadRequest("A code must be supplied for password reset.");
}
else
{
Input = new InputModel
{
Code = code
};
return Page();
}
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
var user = await _userManager.FindByEmailAsync(Input.Email);
if (user == null)
{
// Don't reveal that the user does not exist
return RedirectToPage("./ResetPasswordConfirmation");
}
var result = await _userManager.ResetPasswordAsync(user, Input.Code, Input.Password);
if (result.Succeeded)
{
return RedirectToPage("./ResetPasswordConfirmation");
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
return Page();
}
}
}

10
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml

@ -0,0 +1,10 @@
@page
@model ResetPasswordConfirmationModel
@{
ViewData["Title"] = "Reset password confirmation";
}
<h2>@ViewData["Title"]</h2>
<p>
Your password has been reset. Please <a asp-page="./Login">click here to log in</a>.
</p>

18
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml.cs

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace EVABackend.Areas.Identity.Pages.Account
{
[AllowAnonymous]
public class ResetPasswordConfirmationModel : PageModel
{
public void OnGet()
{
}
}
}

1
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Account/_ViewImports.cshtml

@ -0,0 +1 @@
@using EVABackend.Areas.Identity.Pages.Account

23
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Error.cshtml

@ -0,0 +1,23 @@
@page
@model ErrorModel
@{
ViewData["Title"] = "Error";
}
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
@if (Model.ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
}
<h3>Development Mode</h3>
<p>
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
</p>
<p>
<strong>Development environment should not be enabled in deployed applications</strong>, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>, and restarting the application.
</p>

26
Backend/EVABackend/EVABackend/Areas/Identity/Pages/Error.cshtml.cs

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
namespace EVABackend.Areas.Identity.Pages
{
[AllowAnonymous]
public class ErrorModel : PageModel
{
public string RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
}
}
}

5
Backend/EVABackend/EVABackend/Areas/Identity/Pages/_ViewImports.cshtml

@ -0,0 +1,5 @@
@using Microsoft.AspNetCore.Identity
@using EVABackend.Areas.Identity
@using EVABackend.Areas.Identity.Data
@namespace EVABackend.Areas.Identity.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

4
Backend/EVABackend/EVABackend/Areas/Identity/Pages/_ViewStart.cshtml

@ -0,0 +1,4 @@

@{
Layout = "/Pages/Shared/_Layout.cshtml";
}

194
Backend/EVABackend/EVABackend/Controllers/EVAController.cs

@ -0,0 +1,194 @@
using EVABackend.Areas.Identity.Data;
using EVABackend.Models;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System.Linq;
using System.Threading.Tasks;
namespace EVABackend.Controllers
{
[ApiController]
public class EVAController : ControllerBase
{
private readonly EVAContext _context;
private readonly UserManager<EVABackendUser> _userManager;
private readonly RoleManager<IdentityRole> _roleManager;
private readonly SignInManager<EVABackendUser> _signInManager;
public EVAController(UserManager<EVABackendUser> userManager, RoleManager<IdentityRole> roleManager, SignInManager<EVABackendUser> signInManager)
{
_userManager = userManager;
_roleManager = roleManager;
_signInManager = signInManager;
_context = new EVAContext();
}
[HttpGet]
[Route("login")]
[AllowAnonymous]
public ActionResult Login()
{
return Ok(new { status = "Nicht unterstützt" });
}
[HttpGet]
[Route("create_dummy_data")]
[Authorize]
public async Task<ActionResult> CreateDummyData()
{
if (_userManager.FindByNameAsync("Test") == null)
{
var user = new EVABackendUser
{
UserName = "Test",
Email = "info@test.de"
};
await _userManager.CreateAsync(user, "123Abc!&");
}
var roleNames = new string[] { "Schueler", "Verwaltung", "Admin" };
foreach (var roleName in roleNames)
{
if (!(await _roleManager.RoleExistsAsync(roleName)))
{
await _roleManager.CreateAsync(new IdentityRole(roleName));
}
}
await _userManager.AddToRolesAsync((await _userManager.FindByNameAsync("Test")), roleNames);
return Ok();
}
[HttpPost]
[Route("login")]
[AllowAnonymous]
public async Task<ActionResult> Login([FromForm] string username, [FromForm] string password)
{
var result = await _signInManager.PasswordSignInAsync(username, password, false, false);
if (result.Succeeded)
{
return Ok();
}
return Unauthorized();
}
[HttpPost]
[Route("logout")]
[Authorize]
public async Task<ActionResult> Logout()
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return Ok();
}
[HttpPost]
[Route("aufnahmeantrag")]
[AllowAnonymous]
public async Task<ActionResult> Aufnahmeantrag(Aufnahmeantrag model)
{
var schueler = new Schueler
{
Name = model.Name,
Vorname = model.Vonname,
IstErmaessigt = model.IstErmaessigt,
Ort = model.Ort,
Bankname = model.Bankname,
BLZ = model.BLZ,
Geburtsdatum = model.Geburtsdatum,
Geburtsort = model.Geburtsort,
Telefon = model.Telefon,
EMail = model.EMail,
PLZ = model.PLZ,
KontoNr = model.KontoNr,
Strasse = model.StrasseHNR
};
await _context.Schueler.AddAsync(schueler);
await _context.SaveChangesAsync();
var instrumente = _context.Instrumente.Where(i => model.Instrumente.Contains(i.Id)).ToList();
var kurs = new Kurs
{
Bestaetigt = false,
Name = model.Name,
Kuendigungsfrist = 2,
UnterrichtsTyp = model.Typ,
Laufzeit = 6
};
await _context.Kurse.AddAsync(kurs);
await _context.SaveChangesAsync();
kurs.Instrumente = instrumente.Select(i => new KursInstrument { InstrumentId = i.Id, KursId = kurs.Id }).ToList();
await _context.KursSchueler.AddAsync(new KursSchueler { KursId = kurs.Id, SchuelerId = schueler.Id });
await _context.SaveChangesAsync();
var antrag = new Antrag
{
UnterrichtTyp = model.Typ,
Schueler = schueler,
KursId = kurs.Id
};
await _context.Antraege.AddAsync(antrag);
await _context.SaveChangesAsync();
antrag.Instrumente = instrumente.Select(i => new AntragInstrument { AntragId = antrag.Id, InstrumentId = i.Id }).ToList();
await _context.SaveChangesAsync();
return Ok();
}
[HttpGet]
[Route("instruments")]
[Authorize]
public ActionResult Instruments()
{
var instruments = _context.Instrumente.ToList();
return Ok(instruments.ToArray());
}
[HttpGet]
[Route("rooms")]
[Authorize(Roles = "Verwaltung")]
public ActionResult Rooms()
{
var rooms = _context.Raeume.ToList();
var model = rooms.Select(r => new
{
RaumID = r.Id,
RaumName= r.Name,
Instrumente = r.Instrumente.Select(i => new
{
InstrumentID = i.InstrumentId,
InstrumentName = i.Instrument.Name
})
});
return Ok(model.ToArray());
}
[HttpPut]
[Route("create_rooms")]
[Authorize(Roles = "Verwaltung")]
public async Task<ActionResult> CreateRooms(CreateRooms model)
{
}
}
}

45
Backend/EVABackend/EVABackend/Controllers/ValuesController.cs

@ -1,45 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace EVABackend.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
return "value";
}
// POST api/values
[HttpPost]
public void Post([FromBody] string value)
{
}
// PUT api/values/5
[HttpPut("{id}")]
public void Put(int id, [FromBody] string value)
{
}
// DELETE api/values/5
[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
}

3
Backend/EVABackend/EVABackend/EVABackend.csproj

@ -7,7 +7,7 @@
</PropertyGroup>
<ItemGroup>
<Folder Include="wwwroot\" />
<Folder Include="Areas\Identity\Services\" />
</ItemGroup>
<ItemGroup>
@ -15,6 +15,7 @@
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.1.1" PrivateAssets="All" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.8" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.7.8" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.1.9" />
</ItemGroup>
</Project>

229
Backend/EVABackend/EVABackend/Migrations/20190612100741_CreateIdentitySchema.Designer.cs generated

@ -0,0 +1,229 @@
// <auto-generated />
using System;
using EVABackend.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace EVABackend.Migrations
{
[DbContext(typeof(EVABackendIdentityContext))]
[Migration("20190612100741_CreateIdentitySchema")]
partial class CreateIdentitySchema
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.1.8-servicing-32085");
modelBuilder.Entity("EVABackend.Areas.Identity.Data.EVABackendUser", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Email")
.HasMaxLength(256);
b.Property<bool>("EmailConfirmed");
b.Property<bool>("LockoutEnabled");
b.Property<DateTimeOffset?>("LockoutEnd");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256);
b.Property<string>("NormalizedUserName")
.HasMaxLength(256);
b.Property<string>("PasswordHash");
b.Property<string>("PhoneNumber");
b.Property<bool>("PhoneNumberConfirmed");
b.Property<string>("SecurityStamp");
b.Property<bool>("TwoFactorEnabled");
b.Property<string>("UserName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasName("UserNameIndex");
b.ToTable("AspNetUsers");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Name")
.HasMaxLength(256);
b.Property<string>("NormalizedName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasName("RoleNameIndex");
b.ToTable("AspNetRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("RoleId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasMaxLength(128);
b.Property<string>("ProviderKey")
.HasMaxLength(128);
b.Property<string>("ProviderDisplayName");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("RoleId");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("LoginProvider")
.HasMaxLength(128);
b.Property<string>("Name")
.HasMaxLength(128);
b.Property<string>("Value");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("EVABackend.Areas.Identity.Data.EVABackendUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("EVABackend.Areas.Identity.Data.EVABackendUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("EVABackend.Areas.Identity.Data.EVABackendUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("EVABackend.Areas.Identity.Data.EVABackendUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

217
Backend/EVABackend/EVABackend/Migrations/20190612100741_CreateIdentitySchema.cs

@ -0,0 +1,217 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace EVABackend.Migrations
{
public partial class CreateIdentitySchema : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AspNetRoles",
columns: table => new
{
Id = table.Column<string>(nullable: false),
Name = table.Column<string>(maxLength: 256, nullable: true),
NormalizedName = table.Column<string>(maxLength: 256, nullable: true),
ConcurrencyStamp = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetUsers",
columns: table => new
{
Id = table.Column<string>(nullable: false),
UserName = table.Column<string>(maxLength: 256, nullable: true),
NormalizedUserName = table.Column<string>(maxLength: 256, nullable: true),
Email = table.Column<string>(maxLength: 256, nullable: true),
NormalizedEmail = table.Column<string>(maxLength: 256, nullable: true),
EmailConfirmed = table.Column<bool>(nullable: false),
PasswordHash = table.Column<string>(nullable: true),
SecurityStamp = table.Column<string>(nullable: true),
ConcurrencyStamp = table.Column<string>(nullable: true),
PhoneNumber = table.Column<string>(nullable: true),
PhoneNumberConfirmed = table.Column<bool>(nullable: false),
TwoFactorEnabled = table.Column<bool>(nullable: false),
LockoutEnd = table.Column<DateTimeOffset>(nullable: true),
LockoutEnabled = table.Column<bool>(nullable: false),
AccessFailedCount = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetRoleClaims",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
RoleId = table.Column<string>(nullable: false),
ClaimType = table.Column<string>(nullable: true),
ClaimValue = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserClaims",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
UserId = table.Column<string>(nullable: false),
ClaimType = table.Column<string>(nullable: true),
ClaimValue = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetUserClaims_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserLogins",
columns: table => new
{
LoginProvider = table.Column<string>(maxLength: 128, nullable: false),
ProviderKey = table.Column<string>(maxLength: 128, nullable: false),
ProviderDisplayName = table.Column<string>(nullable: true),
UserId = table.Column<string>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
table.ForeignKey(
name: "FK_AspNetUserLogins_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserRoles",
columns: table => new
{
UserId = table.Column<string>(nullable: false),
RoleId = table.Column<string>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserTokens",
columns: table => new
{
UserId = table.Column<string>(nullable: false),
LoginProvider = table.Column<string>(maxLength: 128, nullable: false),
Name = table.Column<string>(maxLength: 128, nullable: false),
Value = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
table.ForeignKey(
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_AspNetRoleClaims_RoleId",
table: "AspNetRoleClaims",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "RoleNameIndex",
table: "AspNetRoles",
column: "NormalizedName",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AspNetUserClaims_UserId",
table: "AspNetUserClaims",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserLogins_UserId",
table: "AspNetUserLogins",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserRoles_RoleId",
table: "AspNetUserRoles",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "EmailIndex",
table: "AspNetUsers",
column: "NormalizedEmail");
migrationBuilder.CreateIndex(
name: "UserNameIndex",
table: "AspNetUsers",
column: "NormalizedUserName",
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AspNetRoleClaims");
migrationBuilder.DropTable(
name: "AspNetUserClaims");
migrationBuilder.DropTable(
name: "AspNetUserLogins");
migrationBuilder.DropTable(
name: "AspNetUserRoles");
migrationBuilder.DropTable(
name: "AspNetUserTokens");
migrationBuilder.DropTable(
name: "AspNetRoles");
migrationBuilder.DropTable(
name: "AspNetUsers");
}
}
}

388
Backend/EVABackend/EVABackend/Migrations/EVA/20190612101730_InitialCreate.Designer.cs generated

@ -0,0 +1,388 @@
// <auto-generated />
using System;
using EVABackend.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace EVABackend.Migrations.EVA
{
[DbContext(typeof(EVAContext))]
[Migration("20190612101730_InitialCreate")]
partial class InitialCreate
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.1.8-servicing-32085");
modelBuilder.Entity("EVABackend.Models.Antrag", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("KursId");
b.Property<int?>("SchuelerId");
b.Property<int?>("UnterrichtTypId");
b.HasKey("Id");
b.HasIndex("SchuelerId");
b.HasIndex("UnterrichtTypId");
b.ToTable("Antraege");
});
modelBuilder.Entity("EVABackend.Models.AntragInstrument", b =>
{
b.Property<int?>("InstrumentId");
b.Property<int?>("AntragId");
b.HasKey("InstrumentId", "AntragId");
b.HasIndex("AntragId");
b.ToTable("AntragInstrument");
});
modelBuilder.Entity("EVABackend.Models.Dozent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("BLZ")
.IsRequired();
b.Property<string>("Bankname")
.IsRequired();
b.Property<string>("EMail")
.IsRequired();
b.Property<DateTime>("Geburtsdatum");
b.Property<string>("Geburtsort")
.IsRequired();
b.Property<string>("KontoNr")
.IsRequired();
b.Property<string>("Name")
.IsRequired();
b.Property<string>("Ort")
.IsRequired();
b.Property<int>("PLZ");
b.Property<string>("Strasse")
.IsRequired();
b.Property<decimal>("Stundensatz");
b.Property<string>("Telefon")
.IsRequired();
b.Property<string>("Vorname")
.IsRequired();
b.HasKey("Id");
b.ToTable("Donzenten");
});
modelBuilder.Entity("EVABackend.Models.DozentInstrument", b =>
{
b.Property<int?>("InstrumentId");
b.Property<int?>("DozentId");
b.HasKey("InstrumentId", "DozentId");
b.HasIndex("DozentId");
b.ToTable("DozentInstrument");
});
modelBuilder.Entity("EVABackend.Models.Instrument", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Name")
.IsRequired();
b.HasKey("Id");
b.ToTable("Instrumente");
});
modelBuilder.Entity("EVABackend.Models.Kurs", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<bool>("Bestaetigt");
b.Property<int>("Kuendigungsfrist");
b.Property<int>("Laufzeit");
b.Property<string>("Name")
.IsRequired();
b.Property<int?>("UnterrichtsTypId");
b.HasKey("Id");
b.HasIndex("UnterrichtsTypId");
b.ToTable("Kurse");
});
modelBuilder.Entity("EVABackend.Models.KursInstrument", b =>
{
b.Property<int?>("InstrumentId");
b.Property<int?>("KursId");
b.HasKey("InstrumentId", "KursId");
b.HasIndex("KursId");
b.ToTable("KursInstrument");
});
modelBuilder.Entity("EVABackend.Models.KursSchueler", b =>
{
b.Property<int>("SchuelerId");
b.Property<int>("KursId");
b.Property<int?>("DozentId");
b.HasKey("SchuelerId", "KursId");
b.HasIndex("DozentId");
b.HasIndex("KursId");
b.ToTable("KursSchueler");
});
modelBuilder.Entity("EVABackend.Models.Raum", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<bool>("Belegt");
b.Property<string>("Name")
.IsRequired();
b.HasKey("Id");
b.ToTable("Raeume");
});
modelBuilder.Entity("EVABackend.Models.RaumInstrument", b =>
{
b.Property<int?>("InstrumentId");
b.Property<int?>("RaumId");
b.HasKey("InstrumentId", "RaumId");
b.HasIndex("RaumId");
b.ToTable("RaumInstrument");
});
modelBuilder.Entity("EVABackend.Models.Schueler", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("BLZ")
.IsRequired();
b.Property<string>("Bankname")
.IsRequired();
b.Property<string>("EMail")
.IsRequired();
b.Property<DateTime>("Geburtsdatum");
b.Property<string>("Geburtsort")
.IsRequired();
b.Property<bool>("IstErmaessigt");
b.Property<string>("KontoNr")
.IsRequired();
b.Property<string>("Name")
.IsRequired();
b.Property<string>("Ort")
.IsRequired();
b.Property<int>("PLZ");
b.Property<string>("Strasse")
.IsRequired();
b.Property<string>("Telefon")
.IsRequired();
b.Property<string>("Vorname")
.IsRequired();
b.HasKey("Id");
b.ToTable("Schueler");
});
modelBuilder.Entity("EVABackend.Models.Unterricht", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("KursId");
b.Property<string>("Notiz");
b.Property<int?>("RaumId");
b.Property<DateTime>("Startzeit");
b.HasKey("Id");
b.HasIndex("KursId");
b.HasIndex("RaumId");
b.ToTable("Unterrichte");
});
modelBuilder.Entity("EVABackend.Models.UnterrichtTyp", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Name")
.IsRequired();
b.HasKey("Id");
b.ToTable("UnterrichtTypen");
});
modelBuilder.Entity("EVABackend.Models.Antrag", b =>
{
b.HasOne("EVABackend.Models.Schueler", "Schueler")
.WithMany()
.HasForeignKey("SchuelerId");
b.HasOne("EVABackend.Models.UnterrichtTyp", "UnterrichtTyp")
.WithMany()
.HasForeignKey("UnterrichtTypId");
});
modelBuilder.Entity("EVABackend.Models.AntragInstrument", b =>
{
b.HasOne("EVABackend.Models.Antrag", "Antrag")
.WithMany("Instrumente")
.HasForeignKey("AntragId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("EVABackend.Models.Instrument", "Instrument")
.WithMany()
.HasForeignKey("InstrumentId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("EVABackend.Models.DozentInstrument", b =>
{
b.HasOne("EVABackend.Models.Dozent", "Dozent")
.WithMany("Instrumente")
.HasForeignKey("DozentId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("EVABackend.Models.Instrument", "Instrument")
.WithMany()
.HasForeignKey("InstrumentId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("EVABackend.Models.Kurs", b =>
{
b.HasOne("EVABackend.Models.UnterrichtTyp", "UnterrichtsTyp")
.WithMany()
.HasForeignKey("UnterrichtsTypId");
});
modelBuilder.Entity("EVABackend.Models.KursInstrument", b =>
{
b.HasOne("EVABackend.Models.Instrument", "Instrument")
.WithMany()
.HasForeignKey("InstrumentId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("EVABackend.Models.Kurs", "Kurs")
.WithMany("Instrumente")
.HasForeignKey("KursId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("EVABackend.Models.KursSchueler", b =>
{
b.HasOne("EVABackend.Models.Dozent")
.WithMany("KursSchueler")
.HasForeignKey("DozentId");
b.HasOne("EVABackend.Models.Kurs", "Kurs")
.WithMany("KursSchueler")
.HasForeignKey("KursId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("EVABackend.Models.Schueler", "Schueler")
.WithMany("KursSchueler")
.HasForeignKey("SchuelerId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("EVABackend.Models.RaumInstrument", b =>
{
b.HasOne("EVABackend.Models.Instrument", "Instrument")
.WithMany()
.HasForeignKey("InstrumentId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("EVABackend.Models.Raum", "Raum")
.WithMany("Instrumente")
.HasForeignKey("RaumId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("EVABackend.Models.Unterricht", b =>
{
b.HasOne("EVABackend.Models.Kurs")
.WithMany("Unterrichte")
.HasForeignKey("KursId");
b.HasOne("EVABackend.Models.Raum", "Raum")
.WithMany()
.HasForeignKey("RaumId");
});
#pragma warning restore 612, 618
}
}
}

403
Backend/EVABackend/EVABackend/Migrations/EVA/20190612101730_InitialCreate.cs

@ -0,0 +1,403 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace EVABackend.Migrations.EVA
{
public partial class InitialCreate : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Donzenten",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Name = table.Column<string>(nullable: false),
Vorname = table.Column<string>(nullable: false),
Geburtsdatum = table.Column<DateTime>(nullable: false),
Geburtsort = table.Column<string>(nullable: false),
PLZ = table.Column<int>(nullable: false),
Ort = table.Column<string>(nullable: false),
Strasse = table.Column<string>(nullable: false),
Telefon = table.Column<string>(nullable: false),
EMail = table.Column<string>(nullable: false),
Bankname = table.Column<string>(nullable: false),
BLZ = table.Column<string>(nullable: false),
KontoNr = table.Column<string>(nullable: false),
Stundensatz = table.Column<decimal>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Donzenten", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Instrumente",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Name = table.Column<string>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Instrumente", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Raeume",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Name = table.Column<string>(nullable: false),
Belegt = table.Column<bool>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Raeume", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Schueler",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Name = table.Column<string>(nullable: false),
Vorname = table.Column<string>(nullable: false),
Geburtsdatum = table.Column<DateTime>(nullable: false),
Geburtsort = table.Column<string>(nullable: false),
PLZ = table.Column<int>(nullable: false),
Ort = table.Column<string>(nullable: false),
Strasse = table.Column<string>(nullable: false),
Telefon = table.Column<string>(nullable: false),
EMail = table.Column<string>(nullable: false),
Bankname = table.Column<string>(nullable: false),
BLZ = table.Column<string>(nullable: false),
KontoNr = table.Column<string>(nullable: false),
IstErmaessigt = table.Column<bool>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Schueler", x => x.Id);
});
migrationBuilder.CreateTable(
name: "UnterrichtTypen",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Name = table.Column<string>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_UnterrichtTypen", x => x.Id);
});
migrationBuilder.CreateTable(
name: "DozentInstrument",
columns: table => new
{
InstrumentId = table.Column<int>(nullable: false),
DozentId = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_DozentInstrument", x => new { x.InstrumentId, x.DozentId });
table.ForeignKey(
name: "FK_DozentInstrument_Donzenten_DozentId",
column: x => x.DozentId,
principalTable: "Donzenten",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_DozentInstrument_Instrumente_InstrumentId",
column: x => x.InstrumentId,
principalTable: "Instrumente",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "RaumInstrument",
columns: table => new
{
InstrumentId = table.Column<int>(nullable: false),
RaumId = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_RaumInstrument", x => new { x.InstrumentId, x.RaumId });
table.ForeignKey(
name: "FK_RaumInstrument_Instrumente_InstrumentId",
column: x => x.InstrumentId,
principalTable: "Instrumente",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_RaumInstrument_Raeume_RaumId",
column: x => x.RaumId,
principalTable: "Raeume",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "Antraege",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
SchuelerId = table.Column<int>(nullable: true),
UnterrichtTypId = table.Column<int>(nullable: true),
KursId = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Antraege", x => x.Id);
table.ForeignKey(
name: "FK_Antraege_Schueler_SchuelerId",
column: x => x.SchuelerId,
principalTable: "Schueler",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_Antraege_UnterrichtTypen_UnterrichtTypId",
column: x => x.UnterrichtTypId,
principalTable: "UnterrichtTypen",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "Kurse",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Name = table.Column<string>(nullable: false),
Laufzeit = table.Column<int>(nullable: false),
Kuendigungsfrist = table.Column<int>(nullable: false),
Bestaetigt = table.Column<bool>(nullable: false),
UnterrichtsTypId = table.Column<int>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Kurse", x => x.Id);
table.ForeignKey(
name: "FK_Kurse_UnterrichtTypen_UnterrichtsTypId",
column: x => x.UnterrichtsTypId,
principalTable: "UnterrichtTypen",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "AntragInstrument",
columns: table => new
{
InstrumentId = table.Column<int>(nullable: false),
AntragId = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AntragInstrument", x => new { x.InstrumentId, x.AntragId });
table.ForeignKey(
name: "FK_AntragInstrument_Antraege_AntragId",
column: x => x.AntragId,
principalTable: "Antraege",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_AntragInstrument_Instrumente_InstrumentId",
column: x => x.InstrumentId,
principalTable: "Instrumente",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "KursInstrument",
columns: table => new
{
InstrumentId = table.Column<int>(nullable: false),
KursId = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_KursInstrument", x => new { x.InstrumentId, x.KursId });
table.ForeignKey(
name: "FK_KursInstrument_Instrumente_InstrumentId",
column: x => x.InstrumentId,
principalTable: "Instrumente",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_KursInstrument_Kurse_KursId",
column: x => x.KursId,
principalTable: "Kurse",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "KursSchueler",
columns: table => new
{
SchuelerId = table.Column<int>(nullable: false),
KursId = table.Column<int>(nullable: false),
DozentId = table.Column<int>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_KursSchueler", x => new { x.SchuelerId, x.KursId });
table.ForeignKey(
name: "FK_KursSchueler_Donzenten_DozentId",
column: x => x.DozentId,
principalTable: "Donzenten",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_KursSchueler_Kurse_KursId",
column: x => x.KursId,
principalTable: "Kurse",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_KursSchueler_Schueler_SchuelerId",
column: x => x.SchuelerId,
principalTable: "Schueler",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "Unterrichte",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Startzeit = table.Column<DateTime>(nullable: false),
Notiz = table.Column<string>(nullable: true),
RaumId = table.Column<int>(nullable: true),
KursId = table.Column<int>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Unterrichte", x => x.Id);
table.ForeignKey(
name: "FK_Unterrichte_Kurse_KursId",
column: x => x.KursId,
principalTable: "Kurse",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_Unterrichte_Raeume_RaumId",
column: x => x.RaumId,
principalTable: "Raeume",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_Antraege_SchuelerId",
table: "Antraege",
column: "SchuelerId");
migrationBuilder.CreateIndex(
name: "IX_Antraege_UnterrichtTypId",
table: "Antraege",
column: "UnterrichtTypId");
migrationBuilder.CreateIndex(
name: "IX_AntragInstrument_AntragId",
table: "AntragInstrument",
column: "AntragId");
migrationBuilder.CreateIndex(
name: "IX_DozentInstrument_DozentId",
table: "DozentInstrument",
column: "DozentId");
migrationBuilder.CreateIndex(
name: "IX_Kurse_UnterrichtsTypId",
table: "Kurse",
column: "UnterrichtsTypId");
migrationBuilder.CreateIndex(
name: "IX_KursInstrument_KursId",
table: "KursInstrument",
column: "KursId");
migrationBuilder.CreateIndex(
name: "IX_KursSchueler_DozentId",
table: "KursSchueler",
column: "DozentId");
migrationBuilder.CreateIndex(
name: "IX_KursSchueler_KursId",
table: "KursSchueler",
column: "KursId");
migrationBuilder.CreateIndex(
name: "IX_RaumInstrument_RaumId",
table: "RaumInstrument",
column: "RaumId");
migrationBuilder.CreateIndex(
name: "IX_Unterrichte_KursId",
table: "Unterrichte",
column: "KursId");
migrationBuilder.CreateIndex(
name: "IX_Unterrichte_RaumId",
table: "Unterrichte",
column: "RaumId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AntragInstrument");
migrationBuilder.DropTable(
name: "DozentInstrument");
migrationBuilder.DropTable(
name: "KursInstrument");
migrationBuilder.DropTable(
name: "KursSchueler");
migrationBuilder.DropTable(
name: "RaumInstrument");
migrationBuilder.DropTable(
name: "Unterrichte");
migrationBuilder.DropTable(
name: "Antraege");
migrationBuilder.DropTable(
name: "Donzenten");
migrationBuilder.DropTable(
name: "Instrumente");
migrationBuilder.DropTable(
name: "Kurse");
migrationBuilder.DropTable(
name: "Raeume");
migrationBuilder.DropTable(
name: "Schueler");
migrationBuilder.DropTable(
name: "UnterrichtTypen");
}
}
}

386
Backend/EVABackend/EVABackend/Migrations/EVA/EVAContextModelSnapshot.cs

@ -0,0 +1,386 @@
// <auto-generated />
using System;
using EVABackend.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace EVABackend.Migrations.EVA
{
[DbContext(typeof(EVAContext))]
partial class EVAContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.1.8-servicing-32085");
modelBuilder.Entity("EVABackend.Models.Antrag", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("KursId");
b.Property<int?>("SchuelerId");
b.Property<int?>("UnterrichtTypId");
b.HasKey("Id");
b.HasIndex("SchuelerId");
b.HasIndex("UnterrichtTypId");
b.ToTable("Antraege");
});
modelBuilder.Entity("EVABackend.Models.AntragInstrument", b =>
{
b.Property<int?>("InstrumentId");
b.Property<int?>("AntragId");
b.HasKey("InstrumentId", "AntragId");
b.HasIndex("AntragId");
b.ToTable("AntragInstrument");
});
modelBuilder.Entity("EVABackend.Models.Dozent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("BLZ")
.IsRequired();
b.Property<string>("Bankname")
.IsRequired();
b.Property<string>("EMail")
.IsRequired();
b.Property<DateTime>("Geburtsdatum");
b.Property<string>("Geburtsort")
.IsRequired();
b.Property<string>("KontoNr")
.IsRequired();
b.Property<string>("Name")
.IsRequired();
b.Property<string>("Ort")
.IsRequired();
b.Property<int>("PLZ");
b.Property<string>("Strasse")
.IsRequired();
b.Property<decimal>("Stundensatz");
b.Property<string>("Telefon")
.IsRequired();
b.Property<string>("Vorname")
.IsRequired();
b.HasKey("Id");
b.ToTable("Donzenten");
});
modelBuilder.Entity("EVABackend.Models.DozentInstrument", b =>
{
b.Property<int?>("InstrumentId");
b.Property<int?>("DozentId");
b.HasKey("InstrumentId", "DozentId");
b.HasIndex("DozentId");
b.ToTable("DozentInstrument");
});
modelBuilder.Entity("EVABackend.Models.Instrument", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Name")
.IsRequired();
b.HasKey("Id");
b.ToTable("Instrumente");
});
modelBuilder.Entity("EVABackend.Models.Kurs", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<bool>("Bestaetigt");
b.Property<int>("Kuendigungsfrist");
b.Property<int>("Laufzeit");
b.Property<string>("Name")
.IsRequired();
b.Property<int?>("UnterrichtsTypId");
b.HasKey("Id");
b.HasIndex("UnterrichtsTypId");
b.ToTable("Kurse");
});
modelBuilder.Entity("EVABackend.Models.KursInstrument", b =>
{
b.Property<int?>("InstrumentId");
b.Property<int?>("KursId");
b.HasKey("InstrumentId", "KursId");
b.HasIndex("KursId");
b.ToTable("KursInstrument");
});
modelBuilder.Entity("EVABackend.Models.KursSchueler", b =>
{
b.Property<int>("SchuelerId");
b.Property<int>("KursId");
b.Property<int?>("DozentId");
b.HasKey("SchuelerId", "KursId");
b.HasIndex("DozentId");
b.HasIndex("KursId");
b.ToTable("KursSchueler");
});
modelBuilder.Entity("EVABackend.Models.Raum", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<bool>("Belegt");
b.Property<string>("Name")
.IsRequired();
b.HasKey("Id");
b.ToTable("Raeume");
});
modelBuilder.Entity("EVABackend.Models.RaumInstrument", b =>
{
b.Property<int?>("InstrumentId");
b.Property<int?>("RaumId");
b.HasKey("InstrumentId", "RaumId");
b.HasIndex("RaumId");
b.ToTable("RaumInstrument");
});
modelBuilder.Entity("EVABackend.Models.Schueler", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("BLZ")
.IsRequired();
b.Property<string>("Bankname")
.IsRequired();
b.Property<string>("EMail")
.IsRequired();
b.Property<DateTime>("Geburtsdatum");
b.Property<string>("Geburtsort")
.IsRequired();
b.Property<bool>("IstErmaessigt");
b.Property<string>("KontoNr")
.IsRequired();
b.Property<string>("Name")
.IsRequired();
b.Property<string>("Ort")
.IsRequired();
b.Property<int>("PLZ");
b.Property<string>("Strasse")
.IsRequired();
b.Property<string>("Telefon")
.IsRequired();
b.Property<string>("Vorname")
.IsRequired();
b.HasKey("Id");
b.ToTable("Schueler");
});
modelBuilder.Entity("EVABackend.Models.Unterricht", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("KursId");
b.Property<string>("Notiz");
b.Property<int?>("RaumId");
b.Property<DateTime>("Startzeit");
b.HasKey("Id");
b.HasIndex("KursId");
b.HasIndex("RaumId");
b.ToTable("Unterrichte");
});
modelBuilder.Entity("EVABackend.Models.UnterrichtTyp", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Name")
.IsRequired();
b.HasKey("Id");
b.ToTable("UnterrichtTypen");
});
modelBuilder.Entity("EVABackend.Models.Antrag", b =>
{
b.HasOne("EVABackend.Models.Schueler", "Schueler")
.WithMany()
.HasForeignKey("SchuelerId");
b.HasOne("EVABackend.Models.UnterrichtTyp", "UnterrichtTyp")
.WithMany()
.HasForeignKey("UnterrichtTypId");
});
modelBuilder.Entity("EVABackend.Models.AntragInstrument", b =>
{
b.HasOne("EVABackend.Models.Antrag", "Antrag")
.WithMany("Instrumente")
.HasForeignKey("AntragId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("EVABackend.Models.Instrument", "Instrument")
.WithMany()
.HasForeignKey("InstrumentId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("EVABackend.Models.DozentInstrument", b =>
{
b.HasOne("EVABackend.Models.Dozent", "Dozent")
.WithMany("Instrumente")
.HasForeignKey("DozentId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("EVABackend.Models.Instrument", "Instrument")
.WithMany()
.HasForeignKey("InstrumentId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("EVABackend.Models.Kurs", b =>
{
b.HasOne("EVABackend.Models.UnterrichtTyp", "UnterrichtsTyp")
.WithMany()
.HasForeignKey("UnterrichtsTypId");
});
modelBuilder.Entity("EVABackend.Models.KursInstrument", b =>
{
b.HasOne("EVABackend.Models.Instrument", "Instrument")
.WithMany()
.HasForeignKey("InstrumentId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("EVABackend.Models.Kurs", "Kurs")
.WithMany("Instrumente")
.HasForeignKey("KursId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("EVABackend.Models.KursSchueler", b =>
{
b.HasOne("EVABackend.Models.Dozent")
.WithMany("KursSchueler")
.HasForeignKey("DozentId");
b.HasOne("EVABackend.Models.Kurs", "Kurs")
.WithMany("KursSchueler")
.HasForeignKey("KursId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("EVABackend.Models.Schueler", "Schueler")
.WithMany("KursSchueler")
.HasForeignKey("SchuelerId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("EVABackend.Models.RaumInstrument", b =>
{
b.HasOne("EVABackend.Models.Instrument", "Instrument")
.WithMany()
.HasForeignKey("InstrumentId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("EVABackend.Models.Raum", "Raum")
.WithMany("Instrumente")
.HasForeignKey("RaumId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("EVABackend.Models.Unterricht", b =>
{
b.HasOne("EVABackend.Models.Kurs")
.WithMany("Unterrichte")
.HasForeignKey("KursId");
b.HasOne("EVABackend.Models.Raum", "Raum")
.WithMany()
.HasForeignKey("RaumId");
});
#pragma warning restore 612, 618
}
}
}

227
Backend/EVABackend/EVABackend/Migrations/EVABackendIdentityContextModelSnapshot.cs

@ -0,0 +1,227 @@
// <auto-generated />
using System;
using EVABackend.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace EVABackend.Migrations
{
[DbContext(typeof(EVABackendIdentityContext))]
partial class EVABackendIdentityContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.1.8-servicing-32085");
modelBuilder.Entity("EVABackend.Areas.Identity.Data.EVABackendUser", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Email")
.HasMaxLength(256);
b.Property<bool>("EmailConfirmed");
b.Property<bool>("LockoutEnabled");
b.Property<DateTimeOffset?>("LockoutEnd");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256);
b.Property<string>("NormalizedUserName")
.HasMaxLength(256);
b.Property<string>("PasswordHash");
b.Property<string>("PhoneNumber");
b.Property<bool>("PhoneNumberConfirmed");
b.Property<string>("SecurityStamp");
b.Property<bool>("TwoFactorEnabled");
b.Property<string>("UserName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasName("UserNameIndex");
b.ToTable("AspNetUsers");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Name")
.HasMaxLength(256);
b.Property<string>("NormalizedName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasName("RoleNameIndex");
b.ToTable("AspNetRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("RoleId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasMaxLength(128);
b.Property<string>("ProviderKey")
.HasMaxLength(128);
b.Property<string>("ProviderDisplayName");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("RoleId");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("LoginProvider")
.HasMaxLength(128);
b.Property<string>("Name")
.HasMaxLength(128);
b.Property<string>("Value");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("EVABackend.Areas.Identity.Data.EVABackendUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("EVABackend.Areas.Identity.Data.EVABackendUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("EVABackend.Areas.Identity.Data.EVABackendUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("EVABackend.Areas.Identity.Data.EVABackendUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

42
Backend/EVABackend/EVABackend/Models/Aufnahmeantrag.cs

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace EVABackend.Models
{
public class Aufnahmeantrag
{
[Required]
public string Name { get; set; }
[Required]
public string Vonname { get; set; }
public bool IstErmaessigt { get; set; }
public DateTime Geburtsdatum { get; set; }
[Required]
public string Geburtsort { get; set; }
[Range(1, 100000)]
public int PLZ { get; set; }
[Required]
public string Ort { get; set; }
[Required]
public string StrasseHNR { get; set; }
[Required]
public string Bankname { get; set; }
[Required]
public string BLZ { get; set; }
[Required]
public string KontoNr { get; set; }
[Required]
public UnterrichtTyp Typ { get; set; }
[Required]
public List<int> Instrumente { get; set; }
[Required]
public string Laufzeit { get; set; }
[Required]
public string KursId { get; set; }
[Required]
public string Telefon { get; set; }
[Required]
public string EMail { get; set; }
}
}

13
Backend/EVABackend/EVABackend/Models/CreateRooms.cs

@ -0,0 +1,13 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace EVABackend.Models
{
public class CreateRooms
{
[Required]
public string Name { get; set; }
[Required]
public List<string> InstrumentIDs { get; set; }
}
}

227
Backend/EVABackend/EVABackend/Models/EVAContext.cs

@ -0,0 +1,227 @@
using System.Reflection.Emit;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace EVABackend.Models
{
public class EVAContext : DbContext
{
public DbSet<Schueler> Schueler { get; set; }
public DbSet<Dozent> Donzenten { get; set; }
public DbSet<Kurs> Kurse { get; set; }
public DbSet<KursSchueler> KursSchueler { get; set; }
public DbSet<Unterricht> Unterrichte { get; set; }
public DbSet<Instrument> Instrumente { get; set; }
public DbSet<Raum> Raeume { get; set; }
public DbSet<Antrag> Antraege { get; set; }
public DbSet<UnterrichtTyp> UnterrichtTypen { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Data Source=eva_ls.db");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<KursSchueler>()
.HasKey(c => new { c.SchuelerId, c.KursId });
modelBuilder.Entity<RaumInstrument>()
.HasKey(c => new { c.InstrumentId, c.RaumId });
modelBuilder.Entity<KursInstrument>()
.HasKey(c => new { c.InstrumentId, c.KursId });
modelBuilder.Entity<DozentInstrument>()
.HasKey(c => new { c.InstrumentId, c.DozentId });
modelBuilder.Entity<AntragInstrument>()
.HasKey(c => new { c.InstrumentId, c.AntragId });
modelBuilder.Entity<KursSchueler>()
.HasOne(c => c.Schueler)
.WithMany(c => c.KursSchueler)
.HasForeignKey(c => c.SchuelerId);
modelBuilder.Entity<KursSchueler>()
.HasOne(c => c.Kurs)
.WithMany(c => c.KursSchueler)
.HasForeignKey(c => c.KursId);
modelBuilder.Entity<Kurs>()
.HasMany(c => c.Instrumente)
.WithOne(c => c.Kurs)
.HasForeignKey(c => c.KursId);
modelBuilder.Entity<Dozent>()
.HasMany(c => c.Instrumente)
.WithOne(c => c.Dozent)
.HasForeignKey(c => c.DozentId);
modelBuilder.Entity<Raum>()
.HasMany(c => c.Instrumente)
.WithOne(c => c.Raum)
.HasForeignKey(c => c.RaumId);
modelBuilder.Entity<Antrag>()
.HasMany(c => c.Instrumente)
.WithOne(c => c.Antrag)
.HasForeignKey(c => c.AntragId);
}
}
public abstract class Person
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Vorname { get; set; }
public DateTime Geburtsdatum { get; set; }
[Required]
public string Geburtsort { get; set; }
public int PLZ { get; set; }
[Required]
public string Ort { get; set; }
[Required]
public string Strasse { get; set; }
[Required]
public string Telefon { get; set; }
[Required]
public string EMail { get; set; }
[Required]
public string Bankname { get; set; }
[Required]
public string BLZ { get; set; }
[Required]
public string KontoNr { get; set; }
public virtual ICollection<KursSchueler> KursSchueler { get; set; }
}
public class Schueler : Person
{
public bool IstErmaessigt { get; set; }
}
public class Dozent : Person
{
public decimal Stundensatz { get; set; }
public virtual ICollection<DozentInstrument> Instrumente { get; set; }
}
public class Kurs
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public int Laufzeit { get; set; }
public int Kuendigungsfrist { get; set; }
public bool Bestaetigt { get; set; }
public UnterrichtTyp UnterrichtsTyp { get; set; }
public virtual ICollection<KursInstrument> Instrumente { get; set; }
public virtual ICollection<Unterricht> Unterrichte { get; set; }
public virtual ICollection<KursSchueler> KursSchueler { get; set; }
}
public class KursSchueler
{
public int SchuelerId { get; set; }
public int KursId { get; set; }
public virtual Schueler Schueler { get; set; }
public virtual Kurs Kurs { get; set; }
}
public class Unterricht
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public DateTime Startzeit { get; set; }
public string Notiz { get; set; }
public Raum Raum { get; set; }
}
public class Raum
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public bool Belegt { get; set; }
public virtual ICollection<RaumInstrument> Instrumente { get; set; }
}
public class Antrag
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public Schueler Schueler { get; set; }
public UnterrichtTyp UnterrichtTyp { get; set; }
public int KursId { get; set; }
public virtual ICollection<AntragInstrument> Instrumente { get; set; }
}
public class UnterrichtTyp
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string Name { get; set; }
}
public class Instrument
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string Name { get; set; }
}
public class KursInstrument
{
public int? InstrumentId { get; set; }
public Instrument Instrument { get; set; }
public int? KursId { get; set; }
public Kurs Kurs { get; set; }
}
public class RaumInstrument
{
public int? InstrumentId { get; set; }
public Instrument Instrument { get; set; }
public int? RaumId { get; set; }
public Raum Raum { get; set; }
}
public class DozentInstrument
{
public int? InstrumentId { get; set; }
public Instrument Instrument { get; set; }
public int? DozentId { get; set; }
public Dozent Dozent { get; set; }
}
public class AntragInstrument
{
public int? InstrumentId { get; set; }
public Instrument Instrument { get; set; }
public int? AntragId { get; set; }
public Antrag Antrag { get; set; }
}
//public enum InstrumentTyp
//{
// Klavier, Schlagzeug, Geige, Gitarre, Band
//}
}

41
Backend/EVABackend/EVABackend/Pages/Shared/_CookieConsentPartial.cshtml

@ -0,0 +1,41 @@
@using Microsoft.AspNetCore.Http.Features
@{
var consentFeature = Context.Features.Get<ITrackingConsentFeature>();
var showBanner = !consentFeature?.CanTrack ?? false;
var cookieString = consentFeature?.CreateConsentCookie();
}
@if (showBanner)
{
<nav id="cookieConsent" class="navbar navbar-default navbar-fixed-top" role="alert">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#cookieConsent .navbar-collapse">
<span class="sr-only">Toggle cookie consent banner</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<span class="navbar-brand"><span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span></span>
</div>
<div class="collapse navbar-collapse">
<p class="navbar-text">
Use this space to summarize your privacy and cookie use policy.
</p>
<div class="navbar-right">
<a asp-controller="Home" asp-action="Privacy" class="btn btn-info navbar-btn">Learn More</a>
<button type="button" class="btn btn-default navbar-btn" data-cookie-string="@cookieString">Accept</button>
</div>
</div>
</div>
</nav>
<script>
(function () {
document.querySelector("#cookieConsent button[data-cookie-string]").addEventListener("click", function (el) {
document.cookie = el.target.dataset.cookieString;
document.querySelector("#cookieConsent").classList.add("hidden");
}, false);
})();
</script>
}

83
Backend/EVABackend/EVABackend/Pages/Shared/_Layout.cshtml

@ -0,0 +1,83 @@
@using Microsoft.AspNetCore.Hosting
@using Microsoft.AspNetCore.Mvc.ViewEngines
@inject IHostingEnvironment Environment
@inject ICompositeViewEngine Engine
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - EVABackend</title>
<environment include="Development">
<link rel="stylesheet" href="~/Identity/lib/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" href="~/Identity/css/site.css" />
</environment>
<environment exclude="Development">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css"
asp-fallback-href="~/Identity/lib/bootstrap/dist/css/bootstrap.min.css"
asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" />
<link rel="stylesheet" href="~/Identity/css/site.min.css" asp-append-version="true" />
</environment>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a href="~/" class="navbar-brand">EVABackend</a>
</div>
<div class="navbar-collapse collapse">
@{
var result = Engine.FindView(ViewContext, "_LoginPartial", isMainPage: false);
}
@if (result.Success)
{
await Html.RenderPartialAsync("_LoginPartial");
}
else
{
throw new InvalidOperationException("The default Identity UI layout requires a partial view '_LoginPartial' " +
"usually located at '/Pages/_LoginPartial' or at '/Views/Shared/_LoginPartial' to work. Based on your configuration " +
$"we have looked at it in the following locations: {System.Environment.NewLine}{string.Join(System.Environment.NewLine, result.SearchedLocations)}.");
}
</div>
</div>
</nav>
<div class="container body-content">
@RenderBody()
<hr />
<footer>
<p>&copy; 2019 - EVABackend</p>
</footer>
</div>
<environment include="Development">
<script src="~/Identity/lib/jquery/dist/jquery.js"></script>
<script src="~/Identity/lib/bootstrap/dist/js/bootstrap.js"></script>
<script src="~/Identity/js/site.js" asp-append-version="true"></script>
</environment>
<environment exclude="Development">
<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.3.1.min.js"
asp-fallback-src="~/Identity/lib/jquery/dist/jquery.min.js"
asp-fallback-test="window.jQuery"
crossorigin="anonymous"
integrity="sha384-tsQFqpEReu7ZLhBV2VZlAu7zcOV+rXbYlF2cqB8txI/8aZajjp4Bqd+V6D5IgvKT">
</script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"
asp-fallback-src="~/Identity/lib/bootstrap/dist/js/bootstrap.min.js"
asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal"
crossorigin="anonymous"
integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd">
</script>
<script src="~/Identity/js/site.min.js" asp-append-version="true"></script>
</environment>
@RenderSection("Scripts", required: false)
</body>
</html>

25
Backend/EVABackend/EVABackend/Pages/Shared/_LoginPartial.cshtml

@ -0,0 +1,25 @@
@using Microsoft.AspNetCore.Identity
@using EVABackend.Areas.Identity.Data
@inject SignInManager<EVABackendUser> SignInManager
@inject UserManager<EVABackendUser> UserManager
@if (SignInManager.IsSignedIn(User))
{
<form asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@Url.Page("/Index", new { area = "" })" method="post" id="logoutForm" class="navbar-right">
<ul class="nav navbar-nav navbar-right">
<li>
<a asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello @UserManager.GetUserName(User)!</a>
</li>
<li>
<button type="submit" class="btn btn-link navbar-btn navbar-link">Logout</button>
</li>
</ul>
</form>
}
else
{
<ul class="nav navbar-nav navbar-right">
<li><a asp-area="Identity" asp-page="/Account/Register">Register</a></li>
<li><a asp-area="Identity" asp-page="/Account/Login">Login</a></li>
</ul>
}

18
Backend/EVABackend/EVABackend/Pages/Shared/_ValidationScriptsPartial.cshtml

@ -0,0 +1,18 @@
<environment include="Development">
<script src="~/Identity/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
</environment>
<environment exclude="Development">
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.17.0/jquery.validate.min.js"
asp-fallback-src="~/Identity/lib/jquery-validation/dist/jquery.validate.min.js"
asp-fallback-test="window.jQuery && window.jQuery.validator"
crossorigin="anonymous"
integrity="sha384-rZfj/ogBloos6wzLGpPkkOr/gpkBNLZ6b6yLy4o+ok+t/SAKlL5mvXLr0OXNi1Hp">
</script>
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validation.unobtrusive/3.2.9/jquery.validate.unobtrusive.min.js"
asp-fallback-src="~/Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"
asp-fallback-test="window.jQuery && window.jQuery.validator && window.jQuery.validator.unobtrusive"
crossorigin="anonymous"
integrity="sha384-ifv0TYDWxBHzvAk2Z0n8R434FL1Rlv/Av18DXE43N/1rvHyOG4izKst0f2iSLdds">
</script>
</environment>

5
Backend/EVABackend/EVABackend/Pages/_ViewImports.cshtml

@ -0,0 +1,5 @@
@using Microsoft.AspNetCore.Identity
@using EVABackend.Areas.Identity
@using EVABackend.Areas.Identity.Data
@namespace EVABackend.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

3
Backend/EVABackend/EVABackend/Pages/_ViewStart.cshtml

@ -0,0 +1,3 @@
@{
Layout = "_Layout";
}

6
Backend/EVABackend/EVABackend/Properties/launchSettings.json

@ -12,7 +12,7 @@
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "api/values",
"launchUrl": "login",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
@ -20,7 +20,7 @@
"EVABackend": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "api/values",
"launchUrl": "login",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
@ -29,7 +29,7 @@
"Docker": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/api/values",
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/login",
"httpPort": 50571,
"useSSL": true,
"sslPort": 44375

26
Backend/EVABackend/EVABackend/ScaffoldingReadme.txt

@ -0,0 +1,26 @@
Support for ASP.NET Core Identity was added to your project
- The code for adding Identity to your project was generated under Areas/Identity.
Configuration of the Identity related services can be found in the Areas/Identity/IdentityHostingStartup.cs file.
If your app was previously configured to use Identity, then you should remove the call to the AddIdentity method from your ConfigureServices method.
The generated UI requires support for static files. To add static files to your app:
1. Call app.UseStaticFiles() from your Configure method
To use ASP.NET Core Identity you also need to enable authentication. To authentication to your app:
1. Call app.UseAuthentication() from your Configure method (after static files)
The generated UI requires MVC. To add MVC to your app:
1. Call services.AddMvc() from your ConfigureServices method
2. Call app.UseMvc() from your Configure method (after authentication)
The generated database code requires Entity Framework Core Migrations. Run the following commands:
1. dotnet ef migrations add CreateIdentitySchema
2. dotnet ef database update
Or from the Visual Studio Package Manager Console:
1. Add-Migration CreateIdentitySchema
2. Update-Database
Apps that use ASP.NET Core Identity should also use HTTPS. To enable HTTPS see https://go.microsoft.com/fwlink/?linkid=848054.

11
Backend/EVABackend/EVABackend/Startup.cs

@ -1,15 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EVABackend.Areas.Identity.Data;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace EVABackend
{
@ -40,6 +35,8 @@ namespace EVABackend
app.UseHsts();
}
app.UseStaticFiles();
app.UseAuthentication();
app.UseHttpsRedirection();
app.UseMvc();
}

7
Backend/EVABackend/EVABackend/appsettings.json

@ -4,5 +4,8 @@
"Default": "Warning"
}
},
"AllowedHosts": "*"
}
"AllowedHosts": "*",
"ConnectionStrings": {
"EVABackendIdentityContextConnection": "DataSource=EVABackend.db"
}
}

BIN
Backend/EVABackend/EVABackend/eva_ls.db

Binary file not shown.

BIN
Backend/EVABackend/EVABackend/eva_users.db

Binary file not shown.

37
Backend/EVABackend/EVABackend/wwwroot/css/site.css

@ -0,0 +1,37 @@
/* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification\
for details on configuring this project to bundle and minify static web assets. */
body {
padding-top: 50px;
padding-bottom: 20px;
}
/* Wrapping element */
/* Set some basic padding to keep content from hitting the edges */
.body-content {
padding-left: 15px;
padding-right: 15px;
}
/* Carousel */
.carousel-caption p {
font-size: 20px;
line-height: 1.4;
}
/* Make .svg files in the carousel display properly in older browsers */
.carousel-inner .item img[src$=".svg"] {
width: 100%;
}
/* QR code generator */
#qrCode {
margin: 15px;
}
/* Hide/rearrange for smaller screens */
@media screen and (max-width: 767px) {
/* Hide captions */
.carousel-caption {
display: none;
}
}

1
Backend/EVABackend/EVABackend/wwwroot/css/site.min.css vendored

@ -0,0 +1 @@
body{padding-top:50px;padding-bottom:20px}.body-content{padding-left:15px;padding-right:15px}.carousel-caption p{font-size:20px;line-height:1.4}.carousel-inner .item img[src$=".svg"]{width:100%}#qrCode{margin:15px}@media screen and (max-width:767px){.carousel-caption{display:none}}

BIN
Backend/EVABackend/EVABackend/wwwroot/favicon.ico

Binary file not shown.

1
Backend/EVABackend/EVABackend/wwwroot/images/banner1.svg

File diff suppressed because one or more lines are too long

1
Backend/EVABackend/EVABackend/wwwroot/images/banner2.svg

File diff suppressed because one or more lines are too long

1
Backend/EVABackend/EVABackend/wwwroot/images/banner3.svg

File diff suppressed because one or more lines are too long

4
Backend/EVABackend/EVABackend/wwwroot/js/site.js

@ -0,0 +1,4 @@
// Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
// for details on configuring this project to bundle and minify static web assets.
// Write your JavaScript code.

1
Backend/EVABackend/EVABackend/wwwroot/js/site.min.js vendored

@ -0,0 +1 @@


21
Backend/EVABackend/EVABackend/wwwroot/lib/bootstrap/LICENSE

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2011-2016 Twitter, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

587
Backend/EVABackend/EVABackend/wwwroot/lib/bootstrap/dist/css/bootstrap-theme.css vendored

@ -0,0 +1,587 @@
/*!
* Bootstrap v3.4.1 (https://getbootstrap.com/)
* Copyright 2011-2019 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
.btn-default,
.btn-primary,
.btn-success,
.btn-info,
.btn-warning,
.btn-danger {
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
}
.btn-default:active,
.btn-primary:active,
.btn-success:active,
.btn-info:active,
.btn-warning:active,
.btn-danger:active,
.btn-default.active,
.btn-primary.active,
.btn-success.active,
.btn-info.active,
.btn-warning.active,
.btn-danger.active {
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
}
.btn-default.disabled,
.btn-primary.disabled,
.btn-success.disabled,
.btn-info.disabled,
.btn-warning.disabled,
.btn-danger.disabled,
.btn-default[disabled],
.btn-primary[disabled],
.btn-success[disabled],
.btn-info[disabled],
.btn-warning[disabled],
.btn-danger[disabled],
fieldset[disabled] .btn-default,
fieldset[disabled] .btn-primary,
fieldset[disabled] .btn-success,
fieldset[disabled] .btn-info,
fieldset[disabled] .btn-warning,
fieldset[disabled] .btn-danger {
-webkit-box-shadow: none;
box-shadow: none;
}
.btn-default .badge,
.btn-primary .badge,
.btn-success .badge,
.btn-info .badge,
.btn-warning .badge,
.btn-danger .badge {
text-shadow: none;
}
.btn:active,
.btn.active {
background-image: none;
}
.btn-default {
background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);
background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0));
background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #dbdbdb;
text-shadow: 0 1px 0 #fff;
border-color: #ccc;
}
.btn-default:hover,
.btn-default:focus {
background-color: #e0e0e0;
background-position: 0 -15px;
}
.btn-default:active,
.btn-default.active {
background-color: #e0e0e0;
border-color: #dbdbdb;
}
.btn-default.disabled,
.btn-default[disabled],
fieldset[disabled] .btn-default,
.btn-default.disabled:hover,
.btn-default[disabled]:hover,
fieldset[disabled] .btn-default:hover,
.btn-default.disabled:focus,
.btn-default[disabled]:focus,
fieldset[disabled] .btn-default:focus,
.btn-default.disabled.focus,
.btn-default[disabled].focus,
fieldset[disabled] .btn-default.focus,
.btn-default.disabled:active,
.btn-default[disabled]:active,
fieldset[disabled] .btn-default:active,
.btn-default.disabled.active,
.btn-default[disabled].active,
fieldset[disabled] .btn-default.active {
background-color: #e0e0e0;
background-image: none;
}
.btn-primary {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));
background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #245580;
}
.btn-primary:hover,
.btn-primary:focus {
background-color: #265a88;
background-position: 0 -15px;
}
.btn-primary:active,
.btn-primary.active {
background-color: #265a88;
border-color: #245580;
}
.btn-primary.disabled,
.btn-primary[disabled],
fieldset[disabled] .btn-primary,
.btn-primary.disabled:hover,
.btn-primary[disabled]:hover,
fieldset[disabled] .btn-primary:hover,
.btn-primary.disabled:focus,
.btn-primary[disabled]:focus,
fieldset[disabled] .btn-primary:focus,
.btn-primary.disabled.focus,
.btn-primary[disabled].focus,
fieldset[disabled] .btn-primary.focus,
.btn-primary.disabled:active,
.btn-primary[disabled]:active,
fieldset[disabled] .btn-primary:active,
.btn-primary.disabled.active,
.btn-primary[disabled].active,
fieldset[disabled] .btn-primary.active {
background-color: #265a88;
background-image: none;
}
.btn-success {
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641));
background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #3e8f3e;
}
.btn-success:hover,
.btn-success:focus {
background-color: #419641;
background-position: 0 -15px;
}
.btn-success:active,
.btn-success.active {
background-color: #419641;
border-color: #3e8f3e;
}
.btn-success.disabled,
.btn-success[disabled],
fieldset[disabled] .btn-success,
.btn-success.disabled:hover,
.btn-success[disabled]:hover,
fieldset[disabled] .btn-success:hover,
.btn-success.disabled:focus,
.btn-success[disabled]:focus,
fieldset[disabled] .btn-success:focus,
.btn-success.disabled.focus,
.btn-success[disabled].focus,
fieldset[disabled] .btn-success.focus,
.btn-success.disabled:active,
.btn-success[disabled]:active,
fieldset[disabled] .btn-success:active,
.btn-success.disabled.active,
.btn-success[disabled].active,
fieldset[disabled] .btn-success.active {
background-color: #419641;
background-image: none;
}
.btn-info {
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2));
background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #28a4c9;
}
.btn-info:hover,
.btn-info:focus {
background-color: #2aabd2;
background-position: 0 -15px;
}
.btn-info:active,
.btn-info.active {
background-color: #2aabd2;
border-color: #28a4c9;
}
.btn-info.disabled,
.btn-info[disabled],
fieldset[disabled] .btn-info,
.btn-info.disabled:hover,
.btn-info[disabled]:hover,
fieldset[disabled] .btn-info:hover,
.btn-info.disabled:focus,
.btn-info[disabled]:focus,
fieldset[disabled] .btn-info:focus,
.btn-info.disabled.focus,
.btn-info[disabled].focus,
fieldset[disabled] .btn-info.focus,
.btn-info.disabled:active,
.btn-info[disabled]:active,
fieldset[disabled] .btn-info:active,
.btn-info.disabled.active,
.btn-info[disabled].active,
fieldset[disabled] .btn-info.active {
background-color: #2aabd2;
background-image: none;
}
.btn-warning {
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316));
background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #e38d13;
}
.btn-warning:hover,
.btn-warning:focus {
background-color: #eb9316;
background-position: 0 -15px;
}
.btn-warning:active,
.btn-warning.active {
background-color: #eb9316;
border-color: #e38d13;
}
.btn-warning.disabled,
.btn-warning[disabled],
fieldset[disabled] .btn-warning,
.btn-warning.disabled:hover,
.btn-warning[disabled]:hover,
fieldset[disabled] .btn-warning:hover,
.btn-warning.disabled:focus,
.btn-warning[disabled]:focus,
fieldset[disabled] .btn-warning:focus,
.btn-warning.disabled.focus,
.btn-warning[disabled].focus,
fieldset[disabled] .btn-warning.focus,
.btn-warning.disabled:active,
.btn-warning[disabled]:active,
fieldset[disabled] .btn-warning:active,
.btn-warning.disabled.active,
.btn-warning[disabled].active,
fieldset[disabled] .btn-warning.active {
background-color: #eb9316;
background-image: none;
}
.btn-danger {
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a));
background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #b92c28;
}
.btn-danger:hover,
.btn-danger:focus {
background-color: #c12e2a;
background-position: 0 -15px;
}
.btn-danger:active,
.btn-danger.active {
background-color: #c12e2a;
border-color: #b92c28;
}
.btn-danger.disabled,
.btn-danger[disabled],
fieldset[disabled] .btn-danger,
.btn-danger.disabled:hover,
.btn-danger[disabled]:hover,
fieldset[disabled] .btn-danger:hover,
.btn-danger.disabled:focus,
.btn-danger[disabled]:focus,
fieldset[disabled] .btn-danger:focus,
.btn-danger.disabled.focus,
.btn-danger[disabled].focus,
fieldset[disabled] .btn-danger.focus,
.btn-danger.disabled:active,
.btn-danger[disabled]:active,
fieldset[disabled] .btn-danger:active,
.btn-danger.disabled.active,
.btn-danger[disabled].active,
fieldset[disabled] .btn-danger.active {
background-color: #c12e2a;
background-image: none;
}
.thumbnail,
.img-thumbnail {
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
}
.dropdown-menu > li > a:hover,
.dropdown-menu > li > a:focus {
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
background-repeat: repeat-x;
background-color: #e8e8e8;
}
.dropdown-menu > .active > a,
.dropdown-menu > .active > a:hover,
.dropdown-menu > .active > a:focus {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
background-repeat: repeat-x;
background-color: #2e6da4;
}
.navbar-default {
background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);
background-image: -o-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#ffffff), to(#f8f8f8));
background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
}
.navbar-default .navbar-nav > .open > a,
.navbar-default .navbar-nav > .active > a {
background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));
background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);
background-repeat: repeat-x;
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);
}
.navbar-brand,
.navbar-nav > li > a {
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);
}
.navbar-inverse {
background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);
background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222));
background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
border-radius: 4px;
}
.navbar-inverse .navbar-nav > .open > a,
.navbar-inverse .navbar-nav > .active > a {
background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);
background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));
background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);
background-repeat: repeat-x;
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);
box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);
}
.navbar-inverse .navbar-brand,
.navbar-inverse .navbar-nav > li > a {
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.navbar-static-top,
.navbar-fixed-top,
.navbar-fixed-bottom {
border-radius: 0;
}
@media (max-width: 767px) {
.navbar .navbar-nav .open .dropdown-menu > .active > a,
.navbar .navbar-nav .open .dropdown-menu > .active > a:hover,
.navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
color: #fff;
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
background-repeat: repeat-x;
}
}
.alert {
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
}
.alert-success {
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc));
background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
background-repeat: repeat-x;
border-color: #b2dba1;
}
.alert-info {
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0));
background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
background-repeat: repeat-x;
border-color: #9acfea;
}
.alert-warning {
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0));
background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
background-repeat: repeat-x;
border-color: #f5e79e;
}
.alert-danger {
background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3));
background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
background-repeat: repeat-x;
border-color: #dca7a7;
}
.progress {
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5));
background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));
background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-success {
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44));
background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-info {
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5));
background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-warning {
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f));
background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-danger {
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c));
background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-striped {
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
}
.list-group {
border-radius: 4px;
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
}
.list-group-item.active,
.list-group-item.active:hover,
.list-group-item.active:focus {
text-shadow: 0 -1px 0 #286090;
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));
background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);
background-repeat: repeat-x;
border-color: #2b669a;
}
.list-group-item.active .badge,
.list-group-item.active:hover .badge,
.list-group-item.active:focus .badge {
text-shadow: none;
}
.panel {
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
.panel-default > .panel-heading {
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
background-repeat: repeat-x;
}
.panel-primary > .panel-heading {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
background-repeat: repeat-x;
}
.panel-success > .panel-heading {
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6));
background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
background-repeat: repeat-x;
}
.panel-info > .panel-heading {
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3));
background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
background-repeat: repeat-x;
}
.panel-warning > .panel-heading {
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc));
background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
background-repeat: repeat-x;
}
.panel-danger > .panel-heading {
background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc));
background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
background-repeat: repeat-x;
}
.well {
background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5));
background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
background-repeat: repeat-x;
border-color: #dcdcdc;
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
}
/*# sourceMappingURL=bootstrap-theme.css.map */

1
Backend/EVABackend/EVABackend/wwwroot/lib/bootstrap/dist/css/bootstrap-theme.css.map vendored

File diff suppressed because one or more lines are too long

6
Backend/EVABackend/EVABackend/wwwroot/lib/bootstrap/dist/css/bootstrap-theme.min.css vendored

File diff suppressed because one or more lines are too long

1
Backend/EVABackend/EVABackend/wwwroot/lib/bootstrap/dist/css/bootstrap-theme.min.css.map vendored

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save