@@ -37,6 +37,20 @@ namespace Teknik.Areas.Error.Controllers | |||
return View(model); | |||
} | |||
[AllowAnonymous] | |||
public ActionResult Http403(Exception exception) | |||
{ | |||
ViewBag.Title = "403 - " + Config.Title; | |||
ViewBag.Message = "Access Denied"; | |||
Response.StatusCode = 404; | |||
ErrorViewModel model = new ErrorViewModel(); | |||
model.Exception = exception; | |||
return View(model); | |||
} | |||
[AllowAnonymous] | |||
public ActionResult Http404(Exception exception) | |||
{ |
@@ -18,10 +18,24 @@ namespace Teknik.Areas.Error | |||
context.MapSubdomainRoute( | |||
"Error.Http404", // Route name | |||
new List<string>() { "*" }, // Subdomains | |||
"Error/404", // URL with parameters | |||
"404", // URL with parameters | |||
new { controller = "Error", action = "Http404" }, // Parameter defaults | |||
new[] { typeof(Controllers.ErrorController).Namespace } | |||
); | |||
context.MapSubdomainRoute( | |||
"Error.Http403", // Route name | |||
new List<string>() { "*" }, // Subdomains | |||
"403", // URL with parameters | |||
new { controller = "Error", action = "Http403" }, // Parameter defaults | |||
new[] { typeof(Controllers.ErrorController).Namespace } | |||
); | |||
context.MapSubdomainRoute( | |||
"Error.Http500", // Route name | |||
new List<string>() { "*" }, // Subdomains | |||
"500", // URL with parameters | |||
new { controller = "Error", action = "Http500" }, // Parameter defaults | |||
new[] { typeof(Controllers.ErrorController).Namespace } | |||
); | |||
} | |||
} | |||
} |
@@ -0,0 +1,27 @@ | |||
@model Teknik.Areas.Error.ViewModels.ErrorViewModel | |||
<div class="container"> | |||
<div class="row"> | |||
<div class="col-md-12"> | |||
<div class="error-template text-center"> | |||
<h1>Sorry Bud!</h1> | |||
<h2>403 Access Denied</h2> | |||
<div class="error-details"> | |||
You do not have access to this resource. Please contact an Administrator if you think this is an error. | |||
</div> | |||
<br /> | |||
<div class="error-actions"> | |||
<a href="@Url.SubRouteUrl("www", "Home.Index")" class="btn btn-primary btn-lg"> | |||
<span class="glyphicon glyphicon-home"></span> | |||
Take Me Home | |||
</a> | |||
<a href="@Url.SubRouteUrl("contact", "Contact.Index")" class="btn btn-default btn-lg"> | |||
<span class="glyphicon glyphicon-envelope"></span> | |||
Contact Support | |||
</a> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
@@ -1,42 +1,171 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Security.Cryptography; | |||
using System.Text; | |||
using System.Web; | |||
using System.Web.Mvc; | |||
using Teknik.Areas.Paste.ViewModels; | |||
using Teknik.Controllers; | |||
using Teknik.Helpers; | |||
using Teknik.Models; | |||
namespace Teknik.Areas.Paste.Controllers | |||
{ | |||
public class PasteController : DefaultController | |||
{ | |||
private TeknikEntities db = new TeknikEntities(); | |||
[AllowAnonymous] | |||
public ActionResult Index() | |||
{ | |||
ViewBag.Title = Config.Title + " Paste"; | |||
PasteViewModel model = new PasteViewModel(); | |||
return View(model); | |||
} | |||
[AllowAnonymous] | |||
public ActionResult Simple() | |||
public ActionResult ViewPaste(string url, string pass) | |||
{ | |||
Models.Paste paste = db.Pastes.Where(p => p.Url == url).FirstOrDefault(); | |||
if (paste != null) | |||
{ | |||
PasteViewModel model = new PasteViewModel(); | |||
model.Url = url; | |||
model.Content = paste.Content; | |||
model.Title = paste.Title; | |||
model.Syntax = paste.Syntax; | |||
model.DatePosted = paste.DatePosted; | |||
// The paste has a password set | |||
if (!string.IsNullOrEmpty(paste.HashedPassword)) | |||
{ | |||
if (string.IsNullOrEmpty(pass) || Helpers.SHA384.Hash(paste.Key, pass) != paste.HashedPassword) | |||
{ | |||
PasswordViewModel passModel = new PasswordViewModel(); | |||
passModel.Url = url; | |||
passModel.CallingView = "ViewPaste"; | |||
// Redirect them to the password request page | |||
return View("~/Areas/Paste/Views/Paste/PasswordNeeded", passModel); | |||
} | |||
// Now we decrypt the content | |||
byte[] data = Encoding.UTF8.GetBytes(paste.Content); | |||
byte[] ivBytes = Encoding.UTF8.GetBytes(paste.IV); | |||
byte[] encData = AES.Decrypt(data, AES.CreateKey(pass, paste.IV, paste.KeySize), ivBytes); | |||
model.Content = Encoding.UTF8.GetString(encData); | |||
} | |||
return View(model); | |||
} | |||
return Redirect(Url.SubRouteUrl("error", "Error.Http404")); | |||
} | |||
[AllowAnonymous] | |||
public ActionResult Simple(string url, string pass) | |||
{ | |||
PasteViewModel model = new PasteViewModel(); | |||
return View(model); | |||
} | |||
[AllowAnonymous] | |||
public ActionResult Raw() | |||
public ActionResult Raw(string url, string pass) | |||
{ | |||
PasteViewModel model = new PasteViewModel(); | |||
return View(model); | |||
// Create File | |||
var cd = new System.Net.Mime.ContentDisposition | |||
{ | |||
FileName = upload.Url, | |||
Inline = true | |||
}; | |||
Response.AppendHeader("Content-Disposition", cd.ToString()); | |||
return File(data, upload.ContentType); | |||
} | |||
[HttpPost] | |||
[AllowAnonymous] | |||
[ValidateAntiForgeryToken] | |||
public ActionResult Paste(string content, string title, string syntax, string password, bool hide = false) | |||
{ | |||
Models.Paste paste = db.Pastes.Create(); | |||
paste.DatePosted = DateTime.Now; | |||
paste.Url = Utility.RandomString(Config.PasteConfig.UrlLength); | |||
// Set the hashed password if one is provided and encrypt stuff | |||
if (!string.IsNullOrEmpty(password)) | |||
{ | |||
string key = Utility.RandomString(Config.PasteConfig.KeySize / 8); | |||
string iv = Utility.RandomString(Config.PasteConfig.BlockSize / 8); | |||
paste.HashedPassword = Helpers.SHA384.Hash(key, password); | |||
// Encrypt Content | |||
byte[] data = Encoding.UTF8.GetBytes(content); | |||
byte[] keyBytes = AES.CreateKey(password, iv, Config.PasteConfig.KeySize); | |||
byte[] ivBytes = Encoding.UTF8.GetBytes(iv); | |||
byte[] encData = AES.Encrypt(data, keyBytes, ivBytes); | |||
content = Encoding.UTF8.GetString(encData); | |||
paste.Key = key; | |||
paste.KeySize = Config.PasteConfig.KeySize; | |||
paste.IV = iv; | |||
paste.BlockSize = Config.PasteConfig.BlockSize; | |||
} | |||
paste.Content = content; | |||
paste.Title = title; | |||
paste.Syntax = syntax; | |||
db.Pastes.Add(paste); | |||
db.SaveChanges(); | |||
return Redirect(Url.SubRouteUrl("paste", "Paste.View", new { url = paste.Url })); | |||
} | |||
[HttpPost] | |||
[AllowAnonymous] | |||
[ValidateAntiForgeryToken] | |||
public ActionResult Paste() | |||
public ActionResult SubmitPassword(string url, string password) | |||
{ | |||
Models.Paste paste = db.Pastes.Where(p => p.Url == url).FirstOrDefault(); | |||
if (paste != null) | |||
{ | |||
if (Helpers.SHA384.Hash(paste.Key, password) == paste.HashedPassword) | |||
{ | |||
return View(model); | |||
} | |||
} | |||
return Redirect(Url.SubRouteUrl("error", "Error.Http404")); | |||
} | |||
private PasteViewModel GetPasteModel(Models.Paste paste, string pass) | |||
{ | |||
return View(); | |||
PasteViewModel model = new PasteViewModel(); | |||
model.Url = paste.Url; | |||
model.Content = paste.Content; | |||
model.Title = paste.Title; | |||
model.Syntax = paste.Syntax; | |||
model.DatePosted = paste.DatePosted; | |||
// The paste has a password set | |||
if (!string.IsNullOrEmpty(paste.HashedPassword)) | |||
{ | |||
if (string.IsNullOrEmpty(pass) || Helpers.SHA384.Hash(paste.Key, pass) != paste.HashedPassword) | |||
{ | |||
// Redirect them to password page | |||
} | |||
// Now we decrypt the content | |||
byte[] data = Encoding.UTF8.GetBytes(paste.Content); | |||
byte[] ivBytes = Encoding.UTF8.GetBytes(paste.IV); | |||
byte[] encData = AES.Decrypt(data, AES.CreateKey(pass, paste.IV, paste.KeySize), ivBytes); | |||
model.Content = Encoding.UTF8.GetString(encData); | |||
} | |||
return model; | |||
} | |||
} | |||
} |
@@ -3,11 +3,38 @@ using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using Teknik.Areas.Profile.Models; | |||
namespace Teknik.Areas.Paste.Models | |||
{ | |||
public class Paste | |||
{ | |||
public int PasteId { get; set; } | |||
public int? UserId { get; set; } | |||
public User User { get; set; } | |||
public DateTime DatePosted { get; set; } | |||
public string Url { get; set; } | |||
public string Content { get; set; } | |||
public string Title { get; set; } | |||
public string Syntax { get; set; } | |||
public string HashedPassword { get; set; } | |||
public string Key { get; set; } | |||
public int KeySize { get; set; } | |||
public string IV { get; set; } | |||
public int BlockSize { get; set; } | |||
public bool Hide { get; set; } | |||
} | |||
} |
@@ -27,7 +27,7 @@ namespace Teknik.Areas.Paste | |||
"Paste.View", // Route name | |||
new List<string>() { "dev", "paste", "p" }, | |||
"{id}", // URL with parameters | |||
new { controller = "Paste", action = "View" }, // Parameter defaults | |||
new { controller = "Paste", action = "ViewPaste" }, // Parameter defaults | |||
new[] { typeof(Controllers.PasteController).Namespace } | |||
); | |||
context.MapSubdomainRoute( | |||
@@ -48,7 +48,7 @@ namespace Teknik.Areas.Paste | |||
"Paste.Action", // Route name | |||
new List<string>() { "dev", "paste", "p" }, | |||
"Action/{action}", // URL with parameters | |||
new { controller = "Paste", action = "Paste" }, // Parameter defaults | |||
new { controller = "Paste", action = string.Empty }, // Parameter defaults | |||
new[] { typeof(Controllers.PasteController).Namespace } | |||
); | |||
@@ -0,0 +1,16 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using Teknik.ViewModels; | |||
namespace Teknik.Areas.Paste.ViewModels | |||
{ | |||
public class PasswordViewModel : ViewModelBase | |||
{ | |||
public string Url { get; set; } | |||
public string CallingView { get; set; } | |||
} | |||
} |
@@ -9,5 +9,10 @@ namespace Teknik.Areas.Paste.ViewModels | |||
{ | |||
public class PasteViewModel : ViewModelBase | |||
{ | |||
public string Url { get; set; } | |||
public string Content { get; set; } | |||
public string Title { get; set; } | |||
public string Syntax { get; set; } | |||
public DateTime DatePosted { get; set; } | |||
} | |||
} |
@@ -8,23 +8,27 @@ | |||
<div class="container"> | |||
<div class="row"> | |||
<div class="col-sm-12"> | |||
<form class="form-horizontal" name="editor" method="post" action="@Url.SubRouteUrl("paste", "Paste.Paste")"> | |||
<form class="form-horizontal" name="editor" method="post" action="@Url.SubRouteUrl("paste", "Paste.Action", new { action = "Paste" })"> | |||
@Html.AntiForgeryToken() | |||
<div class="form-group"> | |||
<div class="col-sm-10 col-sm-offset-1"> | |||
<textarea class="form-control" id="content" rows="20"></textarea> | |||
<textarea class="form-control" name="content" id="content" rows="20"></textarea> | |||
</div> | |||
</div> | |||
<div class="form-group"> | |||
<label for="title" class="col-sm-2 col-sm-offset-2 control-label">Title</label> | |||
<div class="col-sm-6"> | |||
<input type="text" class="form-control" id="title" placeholder="My Awesome Paste"> | |||
<label for="title" class="col-sm-2 col-sm-offset-1 control-label">Title</label> | |||
<div class="col-sm-4"> | |||
<input type="text" class="form-control" name="title" id="title" placeholder="My Awesome Paste"> | |||
</div> | |||
<div class="col-sm-1 col-sm-offset-2"> | |||
<button type="submit" class="btn btn-primary">Submit Paste</button> | |||
</div> | |||
</div> | |||
<div class="form-group"> | |||
<label for="format" class="col-sm-2 col-sm-offset-2 control-label">Format</label> | |||
<div class="col-sm-6"> | |||
<select class="form-control"> | |||
<option value="<auto>">Auto Select</option> | |||
<label for="syntax" class="col-sm-2 col-sm-offset-1 control-label">Syntax</label> | |||
<div class="col-sm-4"> | |||
<select class="form-control" name="syntax" id="syntax"> | |||
<option value="auto-select">Auto Select</option> | |||
@foreach (KeyValuePair<string, string> format in Constants.HIGHLIGHTFORMATS) | |||
{ | |||
<option value="@format.Value">@format.Key</option> | |||
@@ -33,25 +37,20 @@ | |||
</div> | |||
</div> | |||
<div class="form-group"> | |||
<label for="password" class="col-sm-2 col-sm-offset-2 control-label">Password</label> | |||
<div class="col-sm-6"> | |||
<input type="password" class="form-control" id="title" placeholder="Secret"> | |||
<label for="password" class="col-sm-2 col-sm-offset-1 control-label">Password</label> | |||
<div class="col-sm-4"> | |||
<input type="password" class="form-control" name="password" id="password" placeholder="Secret"> | |||
</div> | |||
</div> | |||
<div class="form-group"> | |||
<div class="col-sm-offset-4 col-sm-6"> | |||
<div class="col-sm-offset-3 col-sm-6"> | |||
<div class="checkbox"> | |||
<label> | |||
<input type="checkbox"> Hide from Public List | |||
<input type="checkbox" name="hide" id="hide"> Hide from Public List | |||
</label> | |||
</div> | |||
</div> | |||
</div> | |||
<div class="form-group"> | |||
<div class="col-sm-offset-2 col-sm-10"> | |||
<button type="submit" class="btn btn-default">Submit Paste</button> | |||
</div> | |||
</div> | |||
</form> | |||
</div> | |||
</div> |
@@ -0,0 +1,18 @@ | |||
@model Teknik.Areas.Paste.ViewModels.PasswordViewModel | |||
<div class="container"> | |||
<div class="row text-center"> | |||
<div class="col-sm-6 col-sm-offset-3"> | |||
<form class="form-inline" method="post" action="@Url.SubRouteUrl("paste", "Paste.Action", new { action = "SubmitPassword" })"> | |||
<h3><span class="glyphicon glyphicon-warning-sign"></span> This paste is password protected.</h3> | |||
<input type="hidden" name="url" value="@Model.Url"> | |||
<div class="well no-padding"> | |||
<div class="form-group"> | |||
<input class="form-control" type="password" name="password" placeholder="Password"> | |||
</div> | |||
<button class="btn btn-primary" type="submit">Submit</button> | |||
</div> | |||
</form> | |||
</div> | |||
</div> | |||
</div> |
@@ -0,0 +1 @@ | |||
|
@@ -96,7 +96,7 @@ namespace Teknik.Areas.Upload.Controllers | |||
} | |||
} | |||
} | |||
return RedirectToRoute("*.Error.Http404"); | |||
return Redirect(Url.SubRouteUrl("error", "Error.Http404")); | |||
} | |||
[HttpPost] | |||
@@ -130,7 +130,7 @@ namespace Teknik.Areas.Upload.Controllers | |||
return File(buffer, System.Net.Mime.MediaTypeNames.Application.Octet, file); | |||
} | |||
} | |||
RedirectToAction("Http404", "Error", new { area = "Errors", exception = new Exception("File Not Found") }); | |||
Redirect(Url.SubRouteUrl("error", "Error.Http404")); | |||
return null; | |||
} | |||
@@ -164,7 +164,7 @@ namespace Teknik.Areas.Upload.Controllers | |||
} | |||
return View(model); | |||
} | |||
return RedirectToRoute("*.Error.Http404"); | |||
return RedirectToRoute("Error.Http404"); | |||
} | |||
[HttpPost] |
@@ -19,14 +19,11 @@ namespace Teknik.Configuration | |||
private string _Host; | |||
private SMTPConfig _SMTPConfig; | |||
private UploadConfig _UploadConfig; | |||
private PasteConfig _PasteConfig; | |||
private BlogConfig _BlogConfig; | |||
private ApiConfig _ApiConfig; | |||
private string _SupportEmail; | |||
private string _BitcoinAddress; | |||
private string _BlogTitle; | |||
private string _BlogDescription; | |||
private int _PostsToLoad; | |||
private int _CommentsToLoad; | |||
public bool DevEnvironment { get { return _DevEnvironment; } set { _DevEnvironment = value; } } | |||
@@ -46,11 +43,14 @@ namespace Teknik.Configuration | |||
public string BitcoinAddress { get { return _BitcoinAddress; } set { _BitcoinAddress = value; } } | |||
// Blog Information | |||
public BlogConfig BlogConfig { get { return _BlogConfig; } set { _BlogConfig = value; } } | |||
public BlogConfig BlogConfig { get { return _BlogConfig; } set { _BlogConfig = value; } } | |||
// Upload Configuration | |||
public UploadConfig UploadConfig { get { return _UploadConfig; } set { _UploadConfig = value; } } | |||
// Paste Configuration | |||
public PasteConfig PasteConfig { get { return _PasteConfig; } set { _PasteConfig = value; } } | |||
// Upload Configuration | |||
public ApiConfig ApiConfig { get { return _ApiConfig; } set { _ApiConfig = value; } } | |||
@@ -74,6 +74,7 @@ namespace Teknik.Configuration | |||
SMTPConfig = new SMTPConfig(); | |||
BlogConfig = new BlogConfig(); | |||
UploadConfig = new UploadConfig(); | |||
PasteConfig = new PasteConfig(); | |||
ApiConfig = new ApiConfig(); | |||
SupportEmail = string.Empty; | |||
BitcoinAddress = string.Empty; |
@@ -0,0 +1,22 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
namespace Teknik.Configuration | |||
{ | |||
public class PasteConfig | |||
{ | |||
public int UrlLength { get; set; } | |||
public int KeySize { get; set; } | |||
public int BlockSize { get; set; } | |||
public PasteConfig() | |||
{ | |||
UrlLength = 5; | |||
KeySize = 256; | |||
BlockSize = 128; | |||
} | |||
} | |||
} |
@@ -31,7 +31,7 @@ namespace Teknik.Configuration | |||
MaxUploadSize = 100000000; | |||
UploadDirectory = string.Empty; | |||
FileExtension = "enc"; | |||
UrlLength = 6; | |||
UrlLength = 5; | |||
DeleteKeyLength = 24; | |||
KeySize = 256; | |||
BlockSize = 128; |
@@ -59,5 +59,13 @@ namespace Teknik.Helpers | |||
return cipher.DoFinal(data); | |||
} | |||
public static byte[] CreateKey(string password, string iv, int keySize = 256) | |||
{ | |||
const int Iterations = 300; | |||
byte[] ivBytes = Encoding.UTF8.GetBytes(iv); | |||
var keyGenerator = new Rfc2898DeriveBytes(password, ivBytes, Iterations); | |||
return keyGenerator.GetBytes(keySize / 8); | |||
} | |||
} | |||
} |
@@ -6,19 +6,26 @@ using Teknik.Areas.Profile.Models; | |||
using Teknik.Areas.Contact.Models; | |||
using Teknik.Migrations; | |||
using Teknik.Areas.Upload.Models; | |||
using Teknik.Areas.Paste.Models; | |||
namespace Teknik.Models | |||
{ | |||
public class TeknikEntities : DbContext | |||
{ | |||
// Users | |||
public DbSet<User> Users { get; set; } | |||
public DbSet<Group> Groups { get; set; } | |||
public DbSet<Role> Roles { get; set; } | |||
// Blogs | |||
public DbSet<Blog> Blogs { get; set; } | |||
public DbSet<Post> Posts { get; set; } | |||
public DbSet<Comment> BlogComments { get; set; } | |||
// Contact | |||
public DbSet<Contact> Contact { get; set; } | |||
// Uploads | |||
public DbSet<Upload> Uploads { get; set; } | |||
// Pastes | |||
public DbSet<Paste> Pastes { get; set; } | |||
protected override void OnModelCreating(DbModelBuilder modelBuilder) | |||
{ | |||
@@ -30,6 +37,7 @@ namespace Teknik.Models | |||
modelBuilder.Entity<Comment>().ToTable("BlogComments"); | |||
modelBuilder.Entity<Contact>().ToTable("Contact"); | |||
modelBuilder.Entity<Upload>().ToTable("Uploads"); | |||
modelBuilder.Entity<Paste>().ToTable("Pastes"); | |||
base.OnModelCreating(modelBuilder); | |||
} |
@@ -172,6 +172,7 @@ | |||
<Compile Include="Areas\Paste\Controllers\PasteController.cs" /> | |||
<Compile Include="Areas\Paste\Models\Paste.cs" /> | |||
<Compile Include="Areas\Paste\PasteAreaRegistration.cs" /> | |||
<Compile Include="Areas\Paste\ViewModels\PasswordViewModel.cs" /> | |||
<Compile Include="Areas\Paste\ViewModels\PasteViewModel.cs" /> | |||
<Compile Include="Areas\Privacy\Controllers\PrivacyController.cs" /> | |||
<Compile Include="Areas\Privacy\PrivacyAreaRegistration.cs" /> | |||
@@ -191,6 +192,7 @@ | |||
<Compile Include="Configuration\BlogConfig.cs" /> | |||
<Compile Include="Configuration\Config.cs" /> | |||
<Compile Include="Areas\Blog\Controllers\BlogController.cs" /> | |||
<Compile Include="Configuration\PasteConfig.cs" /> | |||
<Compile Include="Configuration\SMTPConfig.cs" /> | |||
<Compile Include="Configuration\UploadConfig.cs" /> | |||
<Compile Include="Controllers\DefaultController.cs" /> | |||
@@ -389,6 +391,9 @@ | |||
<Content Include="Areas\Paste\Views\web.config" /> | |||
<Content Include="Areas\Paste\Views\Paste\Index.cshtml" /> | |||
<Content Include="Areas\Paste\Views\_ViewStart.cshtml" /> | |||
<Content Include="Areas\Paste\Views\Paste\ViewPaste.cshtml" /> | |||
<Content Include="Areas\Paste\Views\Paste\PasswordNeeded.cshtml" /> | |||
<Content Include="Areas\Error\Views\Error\Http403.cshtml" /> | |||
<None Include="Properties\PublishProfiles\Teknik Dev.pubxml" /> | |||
<None Include="Scripts\jquery-2.1.4.intellisense.js" /> | |||
<Content Include="Scripts\bootbox\bootbox.min.js" /> |