Browse Source

- Added client management to user's settings page.

- Cleaned up identity server pages.
- Added paste migration to service worker.
tags/3.0.0^2
Teknikode 10 months ago
parent
commit
179a27dd26
41 changed files with 1427 additions and 3282 deletions
  1. 6
    0
      IdentityServer/Content/common.css
  2. 3
    2
      IdentityServer/Controllers/ConsentController.cs
  3. 116
    0
      IdentityServer/Controllers/ManageController.cs
  4. 5
    0
      IdentityServer/Models/Manage/CreateClientModel.cs
  5. 12
    0
      IdentityServer/Models/Manage/DeleteClientModel.cs
  6. 13
    0
      IdentityServer/Models/Manage/GetClientModel.cs
  7. 12
    0
      IdentityServer/Models/Manage/GetClientsModel.cs
  8. 3
    4
      IdentityServer/Views/Account/LoggedOut.cshtml
  9. 22
    15
      IdentityServer/Views/Account/Logout.cshtml
  10. 70
    66
      IdentityServer/Views/Consent/Index.cshtml
  11. 18
    18
      IdentityServer/Views/Consent/_ScopeListItem.cshtml
  12. 73
    67
      IdentityServer/Views/Grants/Index.cshtml
  13. 7
    0
      IdentityServer/Views/Home/Index.cshtml
  14. 4
    1
      ServiceWorker/ArgumentOptions.cs
  15. 8
    1
      ServiceWorker/Program.cs
  16. 2
    0
      ServiceWorker/ServiceWorker.csproj
  17. 64
    0
      ServiceWorker/TeknikMigration.cs
  18. 21
    11
      Teknik/Areas/Paste/PasteHelper.cs
  19. 73
    6
      Teknik/Areas/User/Controllers/UserController.cs
  20. 53
    0
      Teknik/Areas/User/Utility/IdentityHelper.cs
  21. 17
    0
      Teknik/Areas/User/ViewModels/ClientViewModel.cs
  22. 4
    2
      Teknik/Areas/User/ViewModels/DeveloperSettingsViewModel.cs
  23. 25
    0
      Teknik/Areas/User/Views/User/Settings/ClientView.cshtml
  24. 37
    4
      Teknik/Areas/User/Views/User/Settings/DeveloperSettings.cshtml
  25. 6
    2
      Teknik/Areas/User/Views/User/Settings/Settings.cshtml
  26. 0
    1005
      Teknik/Data/Migrations/20180624191511_UserLoginInfo.Designer.cs
  27. 0
    40
      Teknik/Data/Migrations/20180624191511_UserLoginInfo.cs
  28. 0
    789
      Teknik/Data/Migrations/20181018071735_IdentityAuth.Designer.cs
  29. 0
    277
      Teknik/Data/Migrations/20181018071735_IdentityAuth.cs
  30. 0
    733
      Teknik/Data/Migrations/20181021081119_RemovedVerificationTables.Designer.cs
  31. 0
    73
      Teknik/Data/Migrations/20181021081119_RemovedVerificationTables.cs
  32. 0
    51
      Teknik/Data/Migrations/20181121074110_PasteFiles.cs
  33. 2
    2
      Teknik/Data/Migrations/20181222043715_InitialMigration.Designer.cs
  34. 641
    0
      Teknik/Data/Migrations/20181222043715_InitialMigration.cs
  35. 0
    86
      Teknik/IdentityServerConfig.cs
  36. 3
    3
      Teknik/Routes.cs
  37. 96
    2
      Teknik/Scripts/User/DeveloperSettings.js
  38. 0
    3
      Teknik/Startup.cs
  39. 4
    0
      Teknik/Teknik.csproj
  40. 0
    19
      Teknik/TeknikMigration.cs
  41. 7
    0
      Teknik/bundleconfig.json

+ 6
- 0
IdentityServer/Content/common.css View File

@@ -17,6 +17,12 @@ label {
position: relative;
top: -10px;
}

.abc-checkbox label {
display: inline-block !important;
padding-left: 10px !important;
}

.logged-out iframe {
display: none;
width: 0;

+ 3
- 2
IdentityServer/Controllers/ConsentController.cs View File

@@ -2,6 +2,7 @@
using IdentityServer4.Stores;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;
using Teknik.Configuration;
using Teknik.IdentityServer.Models;
@@ -42,7 +43,7 @@ namespace Teknik.IdentityServer.Controllers
return View("Index", vm);
}

return View("Error");
throw new ApplicationException($"Unable to load consent view model.");
}

/// <summary>
@@ -69,7 +70,7 @@ namespace Teknik.IdentityServer.Controllers
return View("Index", result.ViewModel);
}

return View("Error");
throw new ApplicationException($"Unable to load consent view model.");
}
}
}

+ 116
- 0
IdentityServer/Controllers/ManageController.cs View File

@@ -3,6 +3,12 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using IdentityServer4;
using IdentityServer4.EntityFramework.DbContexts;
using IdentityServer4.EntityFramework.Entities;
using IdentityServer4.EntityFramework.Mappers;
using IdentityServer4.Models;
using IdentityServer4.Stores;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
@@ -11,6 +17,7 @@ using Teknik.Configuration;
using Teknik.IdentityServer.Models;
using Teknik.IdentityServer.Models.Manage;
using Teknik.Logging;
using Teknik.Utilities;

namespace Teknik.IdentityServer.Controllers
{
@@ -400,6 +407,115 @@ namespace Teknik.IdentityServer.Controllers
return new JsonResult(new { success = false, message = "User does not exist." });
}

[HttpGet]
public async Task<IActionResult> GetClient(string username, string clientId, [FromServices] IClientStore clientStore, [FromServices] ConfigurationDbContext configContext)
{
if (string.IsNullOrEmpty(username))
return new JsonResult(new { success = false, message = "Username is required" });

if (string.IsNullOrEmpty(clientId))
return new JsonResult(new { success = false, message = "Client Id is required" });

var client = configContext.Clients.FirstOrDefault(c =>
c.ClientId == clientId &&
c.Properties.Exists(p =>
p.Key == "username" &&
p.Value.ToLower() == username.ToLower())
);
if (client != null)
{
var foundClient = await clientStore.FindClientByIdAsync(client.ClientId);
return new JsonResult(new { success = true, data = foundClient });
}

return new JsonResult(new { success = false, message = "Client does not exist." });
}

[HttpGet]
public async Task<IActionResult> GetClients(string username, [FromServices] IClientStore clientStore, [FromServices] ConfigurationDbContext configContext)
{
if (string.IsNullOrEmpty(username))
return new JsonResult(new { success = false, message = "Username is required" });

var foundClientIds = configContext.Clients.Where(c =>
c.Properties.Exists(p =>
p.Key == "username" &&
p.Value.ToLower() == username.ToLower())
).Select(c => c.ClientId);
var clients = new List<IdentityServer4.Models.Client>();
foreach (var clientId in foundClientIds)
{
var foundClient = await clientStore.FindClientByIdAsync(clientId);
if (foundClient != null)
clients.Add(foundClient);
}

return new JsonResult(new { success = true, data = clients });
}

[HttpPost]
public IActionResult CreateClient(CreateClientModel model, [FromServices] ConfigurationDbContext configContext)
{
var clientId = StringHelper.RandomString(20, "abcdefghjkmnpqrstuvwxyz1234567890");
var clientSecret = StringHelper.RandomString(40, "abcdefghjkmnpqrstuvwxyz1234567890");

var client = new IdentityServer4.Models.Client
{
Properties = new Dictionary<string, string>()
{
{ "username", model.Username }
},
ClientId = clientId,
ClientName = model.Name,
AllowedGrantTypes = new List<string>()
{
GrantType.AuthorizationCode,
GrantType.ClientCredentials
},

ClientSecrets =
{
new IdentityServer4.Models.Secret(clientSecret.Sha256())
},

RequireConsent = true,

RedirectUris =
{
model.RedirectURI
},

PostLogoutRedirectUris =
{
model.PostLogoutRedirectURI
},

AllowedScopes = model.AllowedScopes,

AllowOfflineAccess = true
};

configContext.Clients.Add(client.ToEntity());
configContext.SaveChanges();

return new JsonResult(new { success = true, data = new { id = clientId, secret = clientSecret } });
}

[HttpPost]
public async Task<IActionResult> DeleteClient(DeleteClientModel model, [FromServices] IClientStore clientStore, [FromServices] ConfigurationDbContext configContext)
{
var foundClient = await clientStore.FindClientByIdAsync(model.ClientId);
if (foundClient != null)
{
configContext.Clients.Remove(foundClient.ToEntity());
configContext.SaveChanges();

return new JsonResult(new { success = true });
}

return new JsonResult(new { success = false, message = "Client does not exist." });
}

private string FormatKey(string unformattedKey)
{
var result = new StringBuilder();

+ 5
- 0
IdentityServer/Models/Manage/CreateClientModel.cs View File

@@ -7,5 +7,10 @@ namespace Teknik.IdentityServer.Models.Manage
{
public class CreateClientModel
{
public string Username { get; set; }
public string Name { get; set; }
public string RedirectURI { get; set; }
public string PostLogoutRedirectURI { get; set; }
public ICollection<string> AllowedScopes { get; set; }
}
}

+ 12
- 0
IdentityServer/Models/Manage/DeleteClientModel.cs View File

@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Teknik.IdentityServer.Models.Manage
{
public class DeleteClientModel
{
public string ClientId { get; set; }
}
}

+ 13
- 0
IdentityServer/Models/Manage/GetClientModel.cs View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Teknik.IdentityServer.Models.Manage
{
public class GetClientModel
{
public string Username { get; set; }
public string ClientID { get; set; }
}
}

+ 12
- 0
IdentityServer/Models/Manage/GetClientsModel.cs View File

@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Teknik.IdentityServer.Models.Manage
{
public class GetClientsModel
{
public string Username { get; set; }
}
}

+ 3
- 4
IdentityServer/Views/Account/LoggedOut.cshtml View File

@@ -8,10 +8,9 @@
<div class="container">
<div class="row">
<div class="col-md-12 text-center">
<h1>
Logout
<small>You are now logged out</small>
</h1>
<h2>
You are now logged out
</h2>

@if (Model.PostLogoutRedirectUri != null)
{

+ 22
- 15
IdentityServer/Views/Account/Logout.cshtml View File

@@ -1,23 +1,30 @@
@model LogoutViewModel

<div class="logout-page">
<div class="page-header">
<h1>Logout</h1>
</div>

<div class="container">
<div class="row">
<div class="col-sm-6">
<p>Would you like to logout of IdentityServer?</p>
<form asp-action="Logout">
<input type="hidden" name="logoutId" value="@Model.LogoutId" />
<fieldset>
<div class="form-group">
<button class="btn btn-primary">Yes</button>
<div class="col-sm-8 col-sm-offset-2 text-center">
<div class="logout-page">
<div class="page-header">
<h1>Logout</h1>
</div>

<div class="row">
<div class="col-sm-8 col-sm-offset-2">
<p>Would you like to logout of IdentityServer?</p>
<form asp-action="Logout">
<input type="hidden" name="logoutId" value="@Model.LogoutId" />
<fieldset>
<div class="form-group">
<button class="btn btn-primary">Yes</button>
</div>
</fieldset>
</form>
</div>
</fieldset>
</form>
</div>
</div>

</div>
</div>
</div>

<bundle src="js/signout-redirect.min.js" append-version="true"></bundle>
<bundle src="js/signout-redirect.min.js" append-version="true"></bundle>

+ 70
- 66
IdentityServer/Views/Consent/Index.cshtml View File

@@ -1,82 +1,86 @@
@model ConsentViewModel

<div class="page-consent">
<div class="row page-header">
<div class="col-sm-10">
@if (Model.ClientLogoUrl != null)
{
<div class="client-logo"><img src="@Model.ClientLogoUrl"></div>
}
<h1>
@Model.ClientName
<small>is requesting your permission</small>
</h1>
</div>
</div>

<div class="container">
<div class="row">
<div class="col-sm-8">
@Html.Partial("_ValidationSummary")
<div class="col-sm-12 text-center">
<div class="page-consent">
<div class="row page-header">
<div class="col-sm-10 col-sm-offset-1">
@if (Model.ClientLogoUrl != null)
{
<div class="client-logo"><img src="@Model.ClientLogoUrl"></div>
}
<h1>
@Model.ClientName
<small>is requesting your permission</small>
</h1>
</div>
</div>

<div class="row">
<div class="col-sm-10 col-sm-offset-1">
@await Html.PartialAsync("_ValidationSummary")

<form asp-action="Index" class="consent-form">
<input type="hidden" asp-for="ReturnUrl" />

<form asp-action="Index" class="consent-form">
<input type="hidden" asp-for="ReturnUrl" />
<div>Uncheck the permissions you do not wish to grant.</div>

<div>Uncheck the permissions you do not wish to grant.</div>
@if (Model.IdentityScopes.Any())
{
<div class="panel panel-default consent-buttons">
<div class="panel-heading">
<span class="glyphicon glyphicon-user"></span>
Personal Information
</div>
<ul class="list-group">
@foreach (var scope in Model.IdentityScopes)
@if (Model.IdentityScopes.Any())
{
@Html.Partial("_ScopeListItem", scope)
<div class="panel panel-default consent-buttons">
<div class="panel-heading">
<span class="glyphicon glyphicon-user"></span>
Personal Information
</div>
<ul class="list-group">
@foreach (var scope in Model.IdentityScopes)
{
@await Html.PartialAsync("_ScopeListItem", scope)
}
</ul>
</div>
}
</ul>
</div>
}

@if (Model.ResourceScopes.Any())
{
<div class="panel panel-default">
<div class="panel-heading">
<span class="glyphicon glyphicon-tasks"></span>
Application Access
</div>
<ul class="list-group">
@foreach (var scope in Model.ResourceScopes)
@if (Model.ResourceScopes.Any())
{
@Html.Partial("_ScopeListItem", scope)
<div class="panel panel-default">
<div class="panel-heading">
<span class="glyphicon glyphicon-tasks"></span>
Application Access
</div>
<ul class="list-group">
@foreach (var scope in Model.ResourceScopes)
{
@await Html.PartialAsync("_ScopeListItem", scope)
}
</ul>
</div>
}
</ul>
</div>
}

@if (Model.AllowRememberConsent)
{
<div class="consent-remember">
<label>
<input class="consent-scopecheck" asp-for="RememberConsent" />
<strong>Remember My Decision</strong>
</label>
</div>
}
@if (Model.AllowRememberConsent)
{
<div class="consent-remember abc-checkbox">
<input class="consent-scopecheck" asp-for="RememberConsent" />
<label asp-for="RememberConsent"><strong>Remember My Decision</strong></label>
</div>
}

<div class="consent-buttons">
<button name="button" value="yes" class="btn btn-primary" autofocus>Yes, Allow</button>
<button name="button" value="no" class="btn">No, Do Not Allow</button>
@if (Model.ClientUrl != null)
{
<a class="pull-right btn btn-default" target="_blank" href="@Model.ClientUrl">
<span class="glyphicon glyphicon-info-sign"></span>
<strong>@Model.ClientName</strong>
</a>
}
<div class="consent-buttons">
<button name="button" value="yes" class="btn btn-primary" autofocus>Yes, Allow</button>
<button name="button" value="no" class="btn">No, Do Not Allow</button>
@if (Model.ClientUrl != null)
{
<a class="pull-right btn btn-default" target="_blank" href="@Model.ClientUrl">
<span class="glyphicon glyphicon-info-sign"></span>
<strong>@Model.ClientName</strong>
</a>
}
</div>
</form>
</div>
</div>
</form>
</div>
</div>
</div>
</div>

+ 18
- 18
IdentityServer/Views/Consent/_ScopeListItem.cshtml View File

@@ -1,30 +1,30 @@
@model ScopeViewModel

<li class="list-group-item">
<label>
<input class="consent-scopecheck"
type="checkbox"
<li class="list-group-item abc-checkbox">
@if (Model.Required)
{
<input type="hidden"
name="ScopesConsented"
id="scopes_@Model.Name"
value="@Model.Name"
checked="@Model.Checked"
disabled="@Model.Required" />
@if (Model.Required)
{
<input type="hidden"
name="ScopesConsented"
value="@Model.Name" />
}
value="@Model.Name" />
}
<input class="consent-scopecheck"
type="checkbox"
name="ScopesConsented"
id="scopes_@Model.Name"
value="@Model.Name"
checked="@Model.Checked"
disabled="@Model.Required" />
<label for="ScopesConsented">
<strong>@Model.DisplayName</strong>
@if (Model.Emphasize)
{
<span class="glyphicon glyphicon-exclamation-sign"></span>
}
@if (Model.Required)
{
<span><em>(required)</em></span>
}
</label>
@if (Model.Required)
{
<span><em>(required)</em></span>
}
@if (Model.Description != null)
{
<div class="consent-description">

+ 73
- 67
IdentityServer/Views/Grants/Index.cshtml View File

@@ -1,79 +1,85 @@
@model GrantsViewModel

<div class="grants">
<div class="row page-header">
<div class="col-sm-10">
<h1>
Client Application Access
</h1>
<div>Below is the list of applications you have given access to and the names of the resources they have access to.</div>
</div>
</div>

@if (Model.Grants.Any() == false)
{
<div class="row">
<div class="col-sm-8">
<div class="alert alert-info">
You have not given access to any applications
</div>
</div>
</div>
}
else
{
foreach (var grant in Model.Grants)
{
<div class="row grant">
<div class="col-sm-2">
@if (grant.ClientLogoUrl != null)
{
<img src="@grant.ClientLogoUrl">
}
</div>
<div class="col-sm-8">
<div class="clientname">@grant.ClientName</div>
<div>
<span class="created">Created:</span> @grant.Created.ToString("yyyy-MM-dd")
<div class="container">
<div class="row">
<div class="col-md-12 text-center">
<div class="grants">
<div class="row page-header">
<div class="col-sm-12">
<h1>
Client Application Access
</h1>
<div>Below is the list of applications you have given access to and the names of the resources they have access to.</div>
</div>
@if (grant.Expires.HasValue)
{
<div>
<span class="expires">Expires:</span> @grant.Expires.Value.ToString("yyyy-MM-dd")
</div>

@if (Model.Grants.Any() == false)
{
<div class="row">
<div class="col-sm-8">
<div class="alert alert-info">
You have not given access to any applications
</div>
</div>
}
@if (grant.IdentityGrantNames.Any())
</div>
}
else
{
foreach (var grant in Model.Grants)
{
<div>
<div class="granttype">Identity Grants</div>
<ul>
@foreach (var name in grant.IdentityGrantNames)
{
<li>@name</li>
<div class="row grant">
<div class="col-sm-2">
@if (grant.ClientLogoUrl != null)
{
<img src="@grant.ClientLogoUrl">
}
</ul>
</div>
}
@if (grant.ApiGrantNames.Any())
{
<div>
<div class="granttype">API Grants</div>
<ul>
@foreach (var name in grant.ApiGrantNames)
</div>
<div class="col-sm-8">
<div class="clientname">@grant.ClientName</div>
<div>
<span class="created">Created:</span> @grant.Created.ToString("yyyy-MM-dd")
</div>
@if (grant.Expires.HasValue)
{
<div>
<span class="expires">Expires:</span> @grant.Expires.Value.ToString("yyyy-MM-dd")
</div>
}
@if (grant.IdentityGrantNames.Any())
{
<li>@name</li>
<div>
<div class="granttype">Identity Grants</div>
<ul>
@foreach (var name in grant.IdentityGrantNames)
{
<li>@name</li>
}
</ul>
</div>
}
</ul>
@if (grant.ApiGrantNames.Any())
{
<div>
<div class="granttype">API Grants</div>
<ul>
@foreach (var name in grant.ApiGrantNames)
{
<li>@name</li>
}
</ul>
</div>
}
</div>
<div class="col-sm-2">
<form asp-action="Revoke">
<input type="hidden" name="clientId" value="@grant.ClientId">
<button class="btn btn-danger">Revoke Access</button>
</form>
</div>
</div>
}
</div>
<div class="col-sm-2">
<form asp-action="Revoke">
<input type="hidden" name="clientId" value="@grant.ClientId">
<button class="btn btn-danger">Revoke Access</button>
</form>
</div>
}
</div>
}
}
</div>
</div>
</div>

+ 7
- 0
IdentityServer/Views/Home/Index.cshtml View File

@@ -27,6 +27,13 @@
where you can find metadata and links to all the endpoints, key material, etc.
</p>
</div>
@if (User.Identity.IsAuthenticated)
{
<div class="col-md-10 col-md-offset-1">
<a class="btn btn-default" href="@Url.Action("Index", "Grants")">View Granted Clients</a>
<a class="btn btn-default" href="@Url.Action("Logout", "Account")">Logout</a>
</div>
}
</div>
</div>
</div>

+ 4
- 1
ServiceWorker/ArgumentOptions.cs View File

@@ -3,7 +3,7 @@ using System;
using System.Collections.Generic;
using System.Text;

namespace ServiceWorker
namespace Teknik.ServiceWorker
{
public class ArgumentOptions
{
@@ -16,6 +16,9 @@ namespace ServiceWorker
[Option('s', "scan", Default = false, Required = false, HelpText = "Scan all uploads for viruses")]
public bool ScanUploads { get; set; }

[Option('m', "migrate", Default = false, Required = false, HelpText = "Migrate everything")]
public bool Migrate { get; set; }

// Omitting long name, default --verbose
[Option(HelpText = "Prints all messages to standard output.")]
public bool Verbose { get; set; }

+ 8
- 1
ServiceWorker/Program.cs View File

@@ -17,7 +17,7 @@ using Teknik.Data;
using Teknik.Utilities;
using Teknik.Utilities.Cryptography;

namespace ServiceWorker
namespace Teknik.ServiceWorker
{
public class Program
{
@@ -57,6 +57,13 @@ namespace ServiceWorker
{
ScanUploads(config, db);
}

// Runs the migration
if (options.Migrate)
{
// Run the overall migration calls
TeknikMigration.RunMigration(db, config);
}
}

Output(string.Format("[{0}] Finished Server Maintenance Process.", DateTime.Now));

+ 2
- 0
ServiceWorker/ServiceWorker.csproj View File

@@ -3,6 +3,8 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<RootNamespace>Teknik.ServiceWorker</RootNamespace>
<AssemblyName>Teknik.ServiceWorker</AssemblyName>
</PropertyGroup>

<ItemGroup>

+ 64
- 0
ServiceWorker/TeknikMigration.cs View File

@@ -0,0 +1,64 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Teknik.Areas.Paste;
using Teknik.Areas.Paste.Models;
using Teknik.Configuration;
using Teknik.Data;
using Teknik.Utilities;

namespace Teknik.ServiceWorker
{
public static class TeknikMigration
{
public static bool RunMigration(TeknikEntities db, Config config)
{
bool success = false;

MigratePastes(db, config);

return success;
}

public static void MigratePastes(TeknikEntities db, Config config)
{
if (!Directory.Exists(config.PasteConfig.PasteDirectory))
{
Directory.CreateDirectory(config.PasteConfig.PasteDirectory);
}

foreach (var paste in db.Pastes)
{
if (!string.IsNullOrEmpty(paste.Content) && string.IsNullOrEmpty(paste.FileName) && string.IsNullOrEmpty(paste.HashedPassword))
{
// Generate a unique file name that does not currently exist
string filePath = FileHelper.GenerateRandomFileName(config.PasteConfig.PasteDirectory, config.PasteConfig.FileExtension, 10);
string fileName = Path.GetFileName(filePath);

string key = PasteHelper.GenerateKey(config.PasteConfig.KeySize);
string iv = PasteHelper.GenerateIV(config.PasteConfig.BlockSize);

// Encrypt the contents to the file
PasteHelper.EncryptContents(paste.Content, filePath, null, key, iv, config.PasteConfig.KeySize, config.PasteConfig.ChunkSize);

// Generate a deletion key
paste.DeleteKey = StringHelper.RandomString(config.PasteConfig.DeleteKeyLength);

paste.Key = key;
paste.KeySize = config.PasteConfig.KeySize;
paste.IV = iv;
paste.BlockSize = config.PasteConfig.BlockSize;

paste.FileName = fileName;
paste.Content = string.Empty;

db.Entry(paste).State = EntityState.Modified;
db.SaveChanges();
}
}
}
}
}

+ 21
- 11
Teknik/Areas/Paste/PasteHelper.cs View File

@@ -68,22 +68,13 @@ namespace Teknik.Areas.Paste
string key = GenerateKey(config.PasteConfig.KeySize);
string iv = GenerateIV(config.PasteConfig.BlockSize);

byte[] ivBytes = Encoding.Unicode.GetBytes(iv);
byte[] keyBytes = AesCounterManaged.CreateKey(key, ivBytes, config.PasteConfig.KeySize);

// Set the hashed password if one is provided and modify the key
if (!string.IsNullOrEmpty(password))
{
paste.HashedPassword = HashPassword(key, password);
keyBytes = AesCounterManaged.CreateKey(password, ivBytes, config.PasteConfig.KeySize);
}

// Encrypt Content
byte[] data = Encoding.Unicode.GetBytes(content);
using (MemoryStream ms = new MemoryStream(data))
{
AesCounterManaged.EncryptToFile(filePath, ms, config.PasteConfig.ChunkSize, keyBytes, ivBytes);
}
// Encrypt the contents to the file
EncryptContents(content, filePath, password, key, iv, config.PasteConfig.KeySize, config.PasteConfig.ChunkSize);

// Generate a deletion key
string delKey = StringHelper.RandomString(config.PasteConfig.DeleteKeyLength);
@@ -127,5 +118,24 @@ namespace Teknik.Areas.Paste
{
return SHA384.Hash(key, password).ToHex();
}

public static void EncryptContents(string content, string filePath, string password, string key, string iv, int keySize, int chunkSize)
{
byte[] ivBytes = Encoding.Unicode.GetBytes(iv);
byte[] keyBytes = AesCounterManaged.CreateKey(key, ivBytes, keySize);

// Set the hashed password if one is provided and modify the key
if (!string.IsNullOrEmpty(password))
{
keyBytes = AesCounterManaged.CreateKey(password, ivBytes, keySize);
}

// Encrypt Content
byte[] data = Encoding.Unicode.GetBytes(content);
using (MemoryStream ms = new MemoryStream(data))
{
AesCounterManaged.EncryptToFile(filePath, ms, chunkSize, keyBytes, ivBytes);
}
}
}
}

+ 73
- 6
Teknik/Areas/User/Controllers/UserController.cs View File

@@ -35,6 +35,7 @@ using IdentityModel;
using System.Security.Cryptography;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.AspNetCore.Http;
using IdentityServer4.Models;

namespace Teknik.Areas.Users.Controllers
{
@@ -406,22 +407,23 @@ namespace Teknik.Areas.Users.Controllers
return new StatusCodeResult(StatusCodes.Status403Forbidden);
}

public IActionResult AccessTokenSettings()
public async Task<IActionResult> DeveloperSettings()
{
string username = User.Identity.Name;
User user = UserHelper.GetUser(_dbContext, username);

if (user != null)
{
ViewBag.Title = "Access Token Settings - " + _config.Title;
ViewBag.Description = "Your " + _config.Title + " Access Token Settings";
ViewBag.Title = "Developer Settings - " + _config.Title;
ViewBag.Description = "Your " + _config.Title + " Developer Settings";

APIClientSettingsViewModel model = new APIClientSettingsViewModel();
model.Page = "AccessTokens";
DeveloperSettingsViewModel model = new DeveloperSettingsViewModel();
model.Page = "Developer";
model.UserID = user.UserId;
model.Username = user.Username;

model.AuthTokens = new List<AuthTokenViewModel>();
model.Clients = new List<ClientViewModel>();
//foreach (AuthToken token in user.AuthTokens)
//{
// AuthTokenViewModel tokenModel = new AuthTokenViewModel();
@@ -432,7 +434,20 @@ namespace Teknik.Areas.Users.Controllers
// model.AuthTokens.Add(tokenModel);
//}

return View("/Areas/User/Views/User/Settings/AccessTokenSettings.cshtml", model);
Client[] clients = await IdentityHelper.GetClients(_config, username);
foreach (Client client in clients)
{
model.Clients.Add(new ClientViewModel()
{
Id = client.ClientId,
Name = client.ClientName,
RedirectURI = string.Join(',', client.RedirectUris),
PostLogoutRedirectURI = string.Join(',', client.PostLogoutRedirectUris),
AllowedScopes = client.AllowedScopes
});
}

return View("/Areas/User/Views/User/Settings/DeveloperSettings.cshtml", model);
}

return new StatusCodeResult(StatusCodes.Status403Forbidden);
@@ -1211,6 +1226,58 @@ namespace Teknik.Areas.Users.Controllers
}
}

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> CreateClient(string name, string redirectUri, string logoutUri, [FromServices] ICompositeViewEngine viewEngine)
{
try
{
// Validate the code with the identity server
var result = await IdentityHelper.CreateClient(_config, User.Identity.Name, name, redirectUri, logoutUri, "openid", "teknik-api.read", "teknik-api.write");

if (result.Success)
{
var client = (JObject)result.Data;

ClientViewModel model = new ClientViewModel();
model.Id = client["id"].ToString();
model.Name = name;
model.RedirectURI = redirectUri;
model.PostLogoutRedirectURI = logoutUri;

string renderedView = await RenderPartialViewToString(viewEngine, "~/Areas/User/Views/User/Settings/ClientView.cshtml", model);

return Json(new { result = true, clientId = client["id"], secret = client["secret"], html = renderedView });
}
return Json(new { error = result.Message });
}
catch (Exception ex)
{
return Json(new { error = ex.GetFullMessage(true) });
}
}

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteClient(string clientId)
{
try
{
// Validate the code with the identity server
var result = await IdentityHelper.DeleteClient(_config, clientId);

if (result.Success)
{
return Json(new { result = true });
}
return Json(new { error = result.Message });
}
catch (Exception ex)
{
return Json(new { error = ex.GetFullMessage(true) });
}
}

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult CreateInviteCodeLink(int inviteCodeId)

+ 53
- 0
Teknik/Areas/User/Utility/IdentityHelper.cs View File

@@ -1,4 +1,5 @@
using IdentityModel.Client;
using IdentityServer4.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
@@ -324,5 +325,57 @@ namespace Teknik.Areas.Users.Utility
}
throw new Exception(response.Message);
}

public static async Task<Client> GetClient(Config config, string username, string clientId)
{
var manageUrl = CreateUrl(config, $"Manage/GetClient?username={username}&clientId={clientId}");

var result = await Get(config, manageUrl);
if (result.Success)
{
return (Client)result.Data;
}
throw new Exception(result.Message);
}

public static async Task<Client[]> GetClients(Config config, string username)
{
var manageUrl = CreateUrl(config, $"Manage/GetClients?username={username}");

var result = await Get(config, manageUrl);
if (result.Success)
{
return ((JArray)result.Data).ToObject<Client[]>();
}
throw new Exception(result.Message);
}

public static async Task<IdentityResult> CreateClient(Config config, string username, string name, string redirectURI, string postLogoutRedirectURI, params string[] allowedScopes)
{
var manageUrl = CreateUrl(config, $"Manage/CreateClient");

var response = await Post(config, manageUrl,
new
{
username = username,
name = name,
redirectURI = redirectURI,
postLogoutRedirectURI = postLogoutRedirectURI,
allowedScopes = allowedScopes
});
return response;
}

public static async Task<IdentityResult> DeleteClient(Config config, string clientId)
{
var manageUrl = CreateUrl(config, $"Manage/DeleteClient");

var response = await Post(config, manageUrl,
new
{
clientId = clientId
});
return response;
}
}
}

+ 17
- 0
Teknik/Areas/User/ViewModels/ClientViewModel.cs View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Teknik.ViewModels;

namespace Teknik.Areas.Users.ViewModels
{
public class ClientViewModel : ViewModelBase
{
public string Id { get; set; }
public string Name { get; set; }
public string RedirectURI { get; set; }
public string PostLogoutRedirectURI { get; set; }
public ICollection<string> AllowedScopes { get; set; }
}
}

Teknik/Areas/User/ViewModels/APIClientSettingsViewModel.cs → Teknik/Areas/User/ViewModels/DeveloperSettingsViewModel.cs View File

@@ -5,14 +5,16 @@ using System.Threading.Tasks;

namespace Teknik.Areas.Users.ViewModels
{
public class APIClientSettingsViewModel : SettingsViewModel
public class DeveloperSettingsViewModel : SettingsViewModel
{

public List<AuthTokenViewModel> AuthTokens { get; set; }
public List<ClientViewModel> Clients { get; set; }

public APIClientSettingsViewModel()
public DeveloperSettingsViewModel()
{
AuthTokens = new List<AuthTokenViewModel>();
Clients = new List<ClientViewModel>();
}
}
}

+ 25
- 0
Teknik/Areas/User/Views/User/Settings/ClientView.cshtml View File

@@ -0,0 +1,25 @@
@model Teknik.Areas.Users.ViewModels.ClientViewModel

<li class="list-group-item" id="client_@Model.Id">
<div class="btn-group btn-group-sm pull-right" role="group" aria-label="...">
<button type="button" class="btn btn-danger text-danger deleteClient" id="deleteClient_@Model.Id" data-clientId="@Model.Id">Delete</button>
</div>
<h3 class="list-group-item-heading" id="clientName_@Model.Name">@Model.Name</h3>
<div class="list-group-item-text">
<div class="row">
<div class="col-sm-4">
<strong>Client Id</strong>: @Model.Id
</div>
<div class="col-sm-8">
<div class="row">
<div class="col-sm-4"><strong>Redirect Url</strong></div>
<div class="col-sm-8">@Model.RedirectURI</div>
</div>
<div class="row">
<div class="col-sm-4"><strong>Logout Url</strong></div>
<div class="col-sm-8">@Model.PostLogoutRedirectURI</div>
</div>
</div>
</div>
</div>
</li>

Teknik/Areas/User/Views/User/Settings/APIClientSettings.cshtml → Teknik/Areas/User/Views/User/Settings/DeveloperSettings.cshtml View File

@@ -1,4 +1,4 @@
@model Teknik.Areas.Users.ViewModels.APIClientSettingsViewModel
@model Teknik.Areas.Users.ViewModels.DeveloperSettingsViewModel

@using Teknik.Areas.Users.ViewModels

@@ -7,13 +7,17 @@
}

<script>
var clearTrustedDevicesURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "ClearTrustedDevices" })';
var generateTokenURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "GenerateToken" })';
var revokeAllTokensURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "RevokeAllTokens" })';
var editTokenNameURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "EditTokenName" })';
var deleteTokenURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "DeleteToken" })';


var createClientURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "CreateClient" })';
var deleteClientURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "DeleteClient" })';
</script>

<!--
<div class="row">
<div class="col-sm-12">
<h2>API Clients</h2>
@@ -23,7 +27,7 @@
<div class="row">
<div class="col-sm-12">
<br />
<label for="authTokens"><h4>API Clients</h4></label><span class="pull-right"><button type="button" class="btn btn-default" id="create_client">Create Client</button> <button type="button" class="btn btn-danger" id="revoke_all_clients">Revoke All</button></span>
<label for="authTokens"><h4>API Clients</h4></label><span class="pull-right"><button type="button" class="btn btn-default" id="create_authToken">Create Auth Token</button> <button type="button" class="btn btn-danger" id="revoke_all_authTokens">Revoke All</button></span>
<div id="authTokens" style="overflow-y: auto; max-height: 400px;">
<ul class="list-group" id="apiClientList">
@if (Model.AuthTokens.Any())
@@ -41,5 +45,34 @@
</div>
</div>
</div>
-->

<div class="row">
<div class="col-sm-12">
<h2>Clients</h2>
<hr />
</div>
</div>
<div class="row">
<div class="col-sm-12">
<br />
<label for="clients"><h4>Clients</h4></label><span class="pull-right"><button type="button" class="btn btn-default" id="create_client">Create Client</button></span>
<div id="clients" style="overflow-y: auto; max-height: 400px;">
<ul class="list-group" id="clientList">
@if (Model.Clients.Any())
{
foreach (ClientViewModel client in Model.Clients)
{
@await Html.PartialAsync("Settings/ClientView", client)
}
}
else
{
<li class="list-group-item text-center" id="noClients">No Clients</li>
}
</ul>
</div>
</div>
</div>

<bundle src="js/user.settings.accessTokenSettings.min.js" append-version="true"></bundle>
<bundle src="js/user.settings.developer.min.js" append-version="true"></bundle>

+ 6
- 2
Teknik/Areas/User/Views/User/Settings/Settings.cshtml View File

@@ -14,14 +14,18 @@
<!--left col-->
<div class="panel panel-default">
<ul class="list-group">
<div class="panel-heading text-center"><strong>Settings</strong></div>
<div class="panel-heading text-center"><strong>Personal Settings</strong></div>
<a href="@Url.SubRouteUrl("user", "User.ProfileSettings")" class="list-group-item @(Model.Page == "Profile" ? "active" : string.Empty)">Profile</a>
<a href="@Url.SubRouteUrl("user", "User.AccountSettings")" class="list-group-item @(Model.Page == "Account" ? "active" : string.Empty)">Account</a>
<a href="@Url.SubRouteUrl("user", "User.SecuritySettings")" class="list-group-item @(Model.Page == "Security" ? "active" : string.Empty)">Security</a>
<a href="@Url.SubRouteUrl("user", "User.InviteSettings")" class="list-group-item @(Model.Page == "Invite" ? "active" : string.Empty)">Invite Codes</a>
<a href="@Url.SubRouteUrl("user", "User.BlogSettings")" class="list-group-item @(Model.Page == "Blog" ? "active" : string.Empty)">Blogging</a>
<a href="@Url.SubRouteUrl("user", "User.UploadSettings")" class="list-group-item @(Model.Page == "Upload" ? "active" : string.Empty)">Uploads</a>
<a href="@Url.SubRouteUrl("user", "User.APIClientSettings")" class="list-group-item @(Model.Page == "APIClients" ? "active" : string.Empty)">API Clients</a>
</ul>
</div>
<div class="panel panel-default">
<ul class="list-group">
<a href="@Url.SubRouteUrl("user", "User.DeveloperSettings")" class="list-group-item @(Model.Page == "Developer" ? "active" : string.Empty)">Developer Settings</a>
</ul>
</div>
</div><!--/col-2-->

+ 0
- 1005
Teknik/Data/Migrations/20180624191511_UserLoginInfo.Designer.cs
File diff suppressed because it is too large
View File


+ 0
- 40
Teknik/Data/Migrations/20180624191511_UserLoginInfo.cs View File

@@ -1,40 +0,0 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;

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

protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "UserLogins");
}
}
}

+ 0
- 789
Teknik/Data/Migrations/20181018071735_IdentityAuth.Designer.cs View File

@@ -1,789 +0,0 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Teknik.Data;

namespace Teknik.Data.Migrations
{
[DbContext(typeof(TeknikEntities))]
[Migration("20181018071735_IdentityAuth")]
partial class IdentityAuth
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.2.0-preview2-35157")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

modelBuilder.Entity("Teknik.Areas.Blog.Models.Blog", b =>
{
b.Property<int>("BlogId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<int>("UserId");

b.HasKey("BlogId");

b.HasIndex("UserId");

b.ToTable("Blogs");
});

modelBuilder.Entity("Teknik.Areas.Blog.Models.BlogPost", b =>
{
b.Property<int>("BlogPostId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<string>("Article");

b.Property<int>("BlogId");

b.Property<DateTime>("DateEdited");

b.Property<DateTime>("DatePosted");

b.Property<DateTime>("DatePublished");

b.Property<bool>("Published");

b.Property<bool>("System");

b.Property<string>("Title");

b.HasKey("BlogPostId");

b.HasIndex("BlogId");

b.ToTable("BlogPosts");
});

modelBuilder.Entity("Teknik.Areas.Blog.Models.BlogPostComment", b =>
{
b.Property<int>("BlogPostCommentId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<string>("Article");

b.Property<int>("BlogPostId");

b.Property<DateTime>("DateEdited");

b.Property<DateTime>("DatePosted");

b.Property<int?>("UserId");

b.HasKey("BlogPostCommentId");

b.HasIndex("BlogPostId");

b.HasIndex("UserId");

b.ToTable("BlogPostComments");
});

modelBuilder.Entity("Teknik.Areas.Blog.Models.BlogPostTag", b =>
{
b.Property<int>("BlogPostTagId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<int>("BlogPostId");

b.Property<string>("Description");

b.Property<string>("Name");

b.HasKey("BlogPostTagId");

b.HasIndex("BlogPostId");

b.ToTable("BlogPostTags");
});

modelBuilder.Entity("Teknik.Areas.Contact.Models.Contact", b =>
{
b.Property<int>("ContactId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<DateTime>("DateAdded");

b.Property<string>("Email");

b.Property<string>("Message");

b.Property<string>("Name");

b.Property<string>("Subject");

b.HasKey("ContactId");

b.ToTable("Contact");
});

modelBuilder.Entity("Teknik.Areas.Paste.Models.Paste", b =>
{
b.Property<int>("PasteId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<int>("BlockSize");

b.Property<string>("Content");

b.Property<DateTime>("DatePosted");

b.Property<DateTime?>("ExpireDate");

b.Property<string>("HashedPassword")
.HasAnnotation("CaseSensitive", true);

b.Property<bool>("Hide");

b.Property<string>("IV")
.HasAnnotation("CaseSensitive", true);

b.Property<string>("Key")
.HasAnnotation("CaseSensitive", true);

b.Property<int>("KeySize");

b.Property<int>("MaxViews");

b.Property<string>("Syntax");

b.Property<string>("Title");

b.Property<string>("Url")
.HasAnnotation("CaseSensitive", true);

b.Property<int?>("UserId");

b.Property<int>("Views");

b.HasKey("PasteId");

b.HasIndex("UserId");

b.ToTable("Pastes");
});

modelBuilder.Entity("Teknik.Areas.Podcast.Models.Podcast", b =>
{
b.Property<int>("PodcastId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<DateTime>("DateEdited");

b.Property<DateTime>("DatePosted");

b.Property<DateTime>("DatePublished");

b.Property<string>("Description");

b.Property<int>("Episode");

b.Property<bool>("Published");

b.Property<string>("Title");

b.HasKey("PodcastId");

b.ToTable("Podcasts");
});

modelBuilder.Entity("Teknik.Areas.Podcast.Models.PodcastComment", b =>
{
b.Property<int>("PodcastCommentId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<string>("Article");

b.Property<DateTime>("DateEdited");

b.Property<DateTime>("DatePosted");

b.Property<int>("PodcastId");

b.Property<int>("UserId");

b.HasKey("PodcastCommentId");

b.HasIndex("PodcastId");

b.HasIndex("UserId");

b.ToTable("PodcastComments");
});

modelBuilder.Entity("Teknik.Areas.Podcast.Models.PodcastFile", b =>
{
b.Property<int>("PodcastFileId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<long>("ContentLength");

b.Property<string>("ContentType");

b.Property<string>("FileName");

b.Property<string>("Path");

b.Property<int>("PodcastId");

b.Property<int>("Size");

b.HasKey("PodcastFileId");

b.HasIndex("PodcastId");

b.ToTable("PodcastFiles");
});

modelBuilder.Entity("Teknik.Areas.Podcast.Models.PodcastTag", b =>
{
b.Property<int>("PodcastTagId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<string>("Description");

b.Property<string>("Name");

b.Property<int>("PodcastId");

b.HasKey("PodcastTagId");

b.HasIndex("PodcastId");

b.ToTable("PodcastTags");
});

modelBuilder.Entity("Teknik.Areas.Shortener.Models.ShortenedUrl", b =>
{
b.Property<int>("ShortenedUrlId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<DateTime>("DateAdded");

b.Property<string>("OriginalUrl");

b.Property<string>("ShortUrl")
.HasAnnotation("CaseSensitive", true);

b.Property<int?>("UserId");

b.Property<int>("Views");

b.HasKey("ShortenedUrlId");

b.HasIndex("UserId");

b.ToTable("ShortenedUrls");
});

modelBuilder.Entity("Teknik.Areas.Stats.Models.Takedown", b =>
{
b.Property<int>("TakedownId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<string>("ActionTaken");

b.Property<DateTime>("DateActionTaken");

b.Property<DateTime>("DateRequested");

b.Property<string>("Reason");

b.Property<string>("Requester");

b.Property<string>("RequesterContact");

b.HasKey("TakedownId");

b.ToTable("Takedowns");
});

modelBuilder.Entity("Teknik.Areas.Stats.Models.Transaction", b =>
{
b.Property<int>("TransactionId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<decimal>("Amount")
.HasColumnType("decimal(19, 5)");

b.Property<int>("Currency");

b.Property<DateTime>("DateSent");

b.Property<string>("Reason");

b.HasKey("TransactionId");

b.ToTable("Transactions");
});

modelBuilder.Entity("Teknik.Areas.Upload.Models.Upload", b =>
{
b.Property<int>("UploadId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<int>("BlockSize");

b.Property<long>("ContentLength");

b.Property<string>("ContentType");

b.Property<DateTime>("DateUploaded");

b.Property<string>("DeleteKey")
.HasAnnotation("CaseSensitive", true);

b.Property<int>("Downloads");

b.Property<string>("FileName")
.HasAnnotation("CaseSensitive", true);

b.Property<string>("IV")
.HasAnnotation("CaseSensitive", true);

b.Property<string>("Key")
.HasAnnotation("CaseSensitive", true);

b.Property<int>("KeySize");

b.Property<int?>("Takedown_TakedownId");

b.Property<string>("Url")
.HasAnnotation("CaseSensitive", true);

b.Property<int?>("UserId");

b.HasKey("UploadId");

b.HasIndex("Takedown_TakedownId");

b.HasIndex("UserId");

b.ToTable("Uploads");
});

modelBuilder.Entity("Teknik.Areas.Users.Models.InviteCode", b =>
{
b.Property<int>("InviteCodeId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<bool>("Active");

b.Property<int?>("ClaimedUserId");

b.Property<string>("Code")
.HasAnnotation("CaseSensitive", true);

b.Property<int?>("OwnerId");

b.HasKey("InviteCodeId");

b.HasIndex("ClaimedUserId")
.IsUnique()
.HasFilter("[ClaimedUserId] IS NOT NULL");

b.HasIndex("OwnerId");

b.ToTable("InviteCodes");
});

modelBuilder.Entity("Teknik.Areas.Users.Models.LoginInfo", b =>
{
b.Property<int>("LoginInfoId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<string>("LoginProvider");

b.Property<string>("ProviderDisplayName");

b.Property<string>("ProviderKey");

b.Property<int>("UserId");

b.HasKey("LoginInfoId");

b.HasIndex("UserId");

b.ToTable("UserLogins");
});

modelBuilder.Entity("Teknik.Areas.Users.Models.RecoveryEmailVerification", b =>
{
b.Property<int>("RecoveryEmailVerificationId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<string>("Code")
.HasAnnotation("CaseSensitive", true);

b.Property<DateTime>("DateCreated");

b.Property<int>("UserId");

b.HasKey("RecoveryEmailVerificationId");

b.HasIndex("UserId");

b.ToTable("RecoveryEmailVerifications");
});

modelBuilder.Entity("Teknik.Areas.Users.Models.ResetPasswordVerification", b =>
{
b.Property<int>("ResetPasswordVerificationId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<string>("Code")
.HasAnnotation("CaseSensitive", true);

b.Property<DateTime>("DateCreated");

b.Property<int>("UserId");

b.HasKey("ResetPasswordVerificationId");

b.HasIndex("UserId");

b.ToTable("ResetPasswordVerifications");
});

modelBuilder.Entity("Teknik.Areas.Users.Models.User", b =>
{
b.Property<int>("UserId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<string>("Username");

b.HasKey("UserId");

b.ToTable("Users");
});

modelBuilder.Entity("Teknik.Areas.Vault.Models.Vault", b =>
{
b.Property<int>("VaultId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<DateTime>("DateCreated");

b.Property<DateTime>("DateEdited");

b.Property<string>("Description");

b.Property<string>("Title");

b.Property<string>("Url");

b.Property<int?>("UserId");

b.Property<int>("Views");

b.HasKey("VaultId");

b.HasIndex("UserId");

b.ToTable("Vaults");
});

modelBuilder.Entity("Teknik.Areas.Vault.Models.VaultItem", b =>
{
b.Property<int>("VaultItemId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b.Property<DateTime>("DateAdded");

b.Property<string>("Description");

b.Property<string>("Discriminator")
.IsRequired();

b.Property<string>("Title");

b.Property<int>("VaultId");

b.HasKey("VaultItemId");

b.HasIndex("VaultId");

b.ToTable("VaultItems");

b.HasDiscriminator<string>("Discriminator").HasValue("VaultItem");
});

modelBuilder.Entity("Teknik.Areas.Vault.Models.PasteVaultItem", b =>
{
b.HasBaseType("Teknik.Areas.Vault.Models.VaultItem");

b.Property<int>("PasteId");

b.HasIndex("PasteId");

b.HasDiscriminator().HasValue("PasteVaultItem");
});

modelBuilder.Entity("Teknik.Areas.Vault.Models.UploadVaultItem", b =>
{
b.HasBaseType("Teknik.Areas.Vault.Models.VaultItem");

b.Property<int>("UploadId");

b.HasIndex("UploadId");

b.HasDiscriminator().HasValue("UploadVaultItem");
});

modelBuilder.Entity("Teknik.Areas.Blog.Models.Blog", b =>
{
b.HasOne("Teknik.Areas.Users.Models.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});

modelBuilder.Entity("Teknik.Areas.Blog.Models.BlogPost", b =>
{
b.HasOne("Teknik.Areas.Blog.Models.Blog", "Blog")
.WithMany("BlogPosts")
.HasForeignKey("BlogId")
.OnDelete(DeleteBehavior.Cascade);
});

modelBuilder.Entity("Teknik.Areas.Blog.Models.BlogPostComment", b =>
{
b.HasOne("Teknik.Areas.Blog.Models.BlogPost", "BlogPost")
.WithMany("Comments")
.HasForeignKey("BlogPostId")
.OnDelete(DeleteBehavior.Cascade);

b.HasOne("Teknik.Areas.Users.Models.User", "User")
.WithMany()
.HasForeignKey("UserId");
});

modelBuilder.Entity("Teknik.Areas.Blog.Models.BlogPostTag", b =>
{
b.HasOne("Teknik.Areas.Blog.Models.BlogPost", "BlogPost")
.WithMany("Tags")
.HasForeignKey("BlogPostId")
.OnDelete(DeleteBehavior.Cascade);
});

modelBuilder.Entity("Teknik.Areas.Paste.Models.Paste", b =>
{
b.HasOne("Teknik.Areas.Users.Models.User", "User")
.WithMany("Pastes")
.HasForeignKey("UserId");
});

modelBuilder.Entity("Teknik.Areas.Podcast.Models.PodcastComment", b =>
{
b.HasOne("Teknik.Areas.Podcast.Models.Podcast", "Podcast")
.WithMany("Comments")
.HasForeignKey("PodcastId")
.OnDelete(DeleteBehavior.Cascade);

b.HasOne("Teknik.Areas.Users.Models.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});

modelBuilder.Entity("Teknik.Areas.Podcast.Models.PodcastFile", b =>
{
b.HasOne("Teknik.Areas.Podcast.Models.Podcast", "Podcast")
.WithMany("Files")
.HasForeignKey("PodcastId")
.OnDelete(DeleteBehavior.Cascade);
});

modelBuilder.Entity("Teknik.Areas.Podcast.Models.PodcastTag", b =>
{
b.HasOne("Teknik.Areas.Podcast.Models.Podcast", "Podcast")
.WithMany("Tags")
.HasForeignKey("PodcastId")
.OnDelete(DeleteBehavior.Cascade);
});

modelBuilder.Entity("Teknik.Areas.Shortener.Models.ShortenedUrl", b =>
{
b.HasOne("Teknik.Areas.Users.Models.User", "User")
.WithMany("ShortenedUrls")
.HasForeignKey("UserId");
});

modelBuilder.Entity("Teknik.Areas.Upload.Models.Upload", b =>
{
b.HasOne("Teknik.Areas.Stats.Models.Takedown")
.WithMany("Attachments")
.HasForeignKey("Takedown_TakedownId");

b.HasOne("Teknik.Areas.Users.Models.User", "User")
.WithMany("Uploads")
.HasForeignKey("UserId");
});

modelBuilder.Entity("Teknik.Areas.Users.Models.InviteCode", b =>
{
b.HasOne("Teknik.Areas.Users.Models.User", "ClaimedUser")
.WithOne("ClaimedInviteCode")
.HasForeignKey("Teknik.Areas.Users.Models.InviteCode", "ClaimedUserId");

b.HasOne("Teknik.Areas.Users.Models.User", "Owner")
.WithMany("OwnedInviteCodes")
.HasForeignKey("OwnerId");
});

modelBuilder.Entity("Teknik.Areas.Users.Models.LoginInfo", b =>
{
b.HasOne("Teknik.Areas.Users.Models.User", "User")
.WithMany("Logins")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});

modelBuilder.Entity("Teknik.Areas.Users.Models.RecoveryEmailVerification", b =>
{
b.HasOne("Teknik.Areas.Users.Models.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});

modelBuilder.Entity("Teknik.Areas.Users.Models.ResetPasswordVerification", b =>
{
b.HasOne("Teknik.Areas.Users.Models.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});

modelBuilder.Entity("Teknik.Areas.Users.Models.User", b =>
{
b.OwnsOne("Teknik.Areas.Users.Models.BlogSettings", "BlogSettings", b1 =>
{
b1.Property<int>("UserId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b1.Property<string>("Description")
.HasColumnName("Description");

b1.Property<string>("Title")
.HasColumnName("Title");

b1.HasKey("UserId");

b1.ToTable("Users");

b1.HasOne("Teknik.Areas.Users.Models.User")
.WithOne("BlogSettings")
.HasForeignKey("Teknik.Areas.Users.Models.BlogSettings", "UserId")
.OnDelete(DeleteBehavior.Cascade);
});

b.OwnsOne("Teknik.Areas.Users.Models.UploadSettings", "UploadSettings", b1 =>
{
b1.Property<int>("UserId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b1.Property<bool>("Encrypt")
.HasColumnName("Encrypt");

b1.HasKey("UserId");

b1.ToTable("Users");

b1.HasOne("Teknik.Areas.Users.Models.User")
.WithOne("UploadSettings")
.HasForeignKey("Teknik.Areas.Users.Models.UploadSettings", "UserId")
.OnDelete(DeleteBehavior.Cascade);
});

b.OwnsOne("Teknik.Areas.Users.Models.UserSettings", "UserSettings", b1 =>
{
b1.Property<int>("UserId")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

b1.Property<string>("About")
.HasColumnName("About");

b1.Property<string>("Quote")
.HasColumnName("Quote");

b1.Property<string>("Website")
.HasColumnName("Website");

b1.HasKey("UserId");

b1.ToTable("Users");

b1.HasOne("Teknik.Areas.Users.Models.User")
.WithOne("UserSettings")
.HasForeignKey("Teknik.Areas.Users.Models.UserSettings", "UserId")
.OnDelete(DeleteBehavior.Cascade);
});
});

modelBuilder.Entity("Teknik.Areas.Vault.Models.Vault", b =>
{
b.HasOne("Teknik.Areas.Users.Models.User", "User")
.WithMany("Vaults")
.HasForeignKey("UserId");
});

modelBuilder.Entity("Teknik.Areas.Vault.Models.VaultItem", b =>
{
b.HasOne("Teknik.Areas.Vault.Models.Vault", "Vault")
.WithMany("VaultItems")
.HasForeignKey("VaultId")
.OnDelete(DeleteBehavior.Cascade);
});

modelBuilder.Entity("Teknik.Areas.Vault.Models.PasteVaultItem", b =>
{
b.HasOne("Teknik.Areas.Paste.Models.Paste", "Paste")
.WithMany("PasteVaultItems")
.HasForeignKey("PasteId")
.OnDelete(DeleteBehavior.Cascade);
});

modelBuilder.Entity("Teknik.Areas.Vault.Models.UploadVaultItem", b =>
{
b.HasOne("Teknik.Areas.Upload.Models.Upload", "Upload")
.WithMany("UploadVaultItems")
.HasForeignKey("UploadId")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

+ 0
- 277
Teknik/Data/Migrations/20181018071735_IdentityAuth.cs View File

@@ -1,277 +0,0 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;

namespace Teknik.Data.Migrations
{
public partial class IdentityAuth : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AuthTokens");

migrationBuilder.DropTable(
name: "TransferTypes");

migrationBuilder.DropTable(
name: "TrustedDevices");

migrationBuilder.DropTable(
name: "UserRoles");

migrationBuilder.DropTable(
name: "Roles");