Refresh Pt 2

This commit is contained in:
Thomas Hounsell 2015-03-21 19:29:21 +00:00
parent a55b8ada0f
commit e11ac1cd38
15 changed files with 3297 additions and 856 deletions

View File

@ -1,10 +1,10 @@
using System;
using RedisAuth;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using BuildFeed.Auth;
namespace BuildFeed.Areas.admin.Controllers
{

View File

@ -1,496 +0,0 @@
using System;
using System.Collections.Specialized;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Web.Security;
using NServiceKit.DataAnnotations;
using NServiceKit.DesignPatterns.Model;
using NServiceKit.Redis;
using Required = System.ComponentModel.DataAnnotations.RequiredAttribute;
namespace BuildFeed.Auth
{
public class RedisMembershipProvider : MembershipProvider
{
private bool _enablePasswordReset = true;
private int _maxInvalidPasswordAttempts = 5;
private int _minRequiredNonAlphanumericCharacters = 1;
private int _minRequriedPasswordLength = 12;
private int _passwordAttemptWindow = 60;
private bool _requiresUniqueEmail = true;
public override string ApplicationName
{
get { return ""; }
set { }
}
public override bool EnablePasswordReset
{
get { return _enablePasswordReset; }
}
public override bool EnablePasswordRetrieval
{
get { return false; }
}
public override int MaxInvalidPasswordAttempts
{
get { return _maxInvalidPasswordAttempts; }
}
public override int MinRequiredNonAlphanumericCharacters
{
get { return _minRequiredNonAlphanumericCharacters; }
}
public override int MinRequiredPasswordLength
{
get { return _minRequriedPasswordLength; }
}
public override int PasswordAttemptWindow
{
get { return _passwordAttemptWindow; }
}
public override MembershipPasswordFormat PasswordFormat
{
get { return MembershipPasswordFormat.Hashed; }
}
public override string PasswordStrengthRegularExpression
{
get { return ""; }
}
public override bool RequiresQuestionAndAnswer
{
get { return false; }
}
public override bool RequiresUniqueEmail
{
get { return _requiresUniqueEmail; }
}
public override void Initialize(string name, NameValueCollection config)
{
if (config == null)
{
throw new ArgumentNullException("config");
}
base.Initialize(name, config);
_enablePasswordReset = tryReadBool(config["enablePasswordReset"], _enablePasswordReset);
_maxInvalidPasswordAttempts = tryReadInt(config["maxInvalidPasswordAttempts"], _maxInvalidPasswordAttempts);
_minRequiredNonAlphanumericCharacters = tryReadInt(config["minRequiredNonAlphanumericCharacters"], _minRequiredNonAlphanumericCharacters);
_minRequriedPasswordLength = tryReadInt(config["minRequriedPasswordLength"], _minRequriedPasswordLength);
_passwordAttemptWindow = tryReadInt(config["passwordAttemptWindow"], _passwordAttemptWindow);
_requiresUniqueEmail = tryReadBool(config["requiresUniqueEmail"], _requiresUniqueEmail);
}
public override bool ChangePassword(string username, string oldPassword, string newPassword)
{
bool isAuthenticated = ValidateUser(username, oldPassword);
if (isAuthenticated)
{
using (RedisClient rClient = new RedisClient(DatabaseConfig.Host, DatabaseConfig.Port, db: DatabaseConfig.Database))
{
var client = rClient.As<RedisMember>();
var rm = client.GetAll().SingleOrDefault(m => m.UserName.ToLower() == username.ToLower());
byte[] salt = new byte[24];
byte[] hash = calculateHash(newPassword, ref salt);
rm.PassSalt = salt;
rm.PassHash = hash;
client.Store(rm);
return true;
}
}
return false;
}
public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
{
throw new NotImplementedException();
}
public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
{
if (password.Length < MinRequiredPasswordLength)
{
status = MembershipCreateStatus.InvalidPassword;
return null;
}
MembershipUser mu = null;
using (RedisClient rClient = new RedisClient(DatabaseConfig.Host, DatabaseConfig.Port, db: DatabaseConfig.Database))
{
var client = rClient.As<RedisMember>();
var users = client.GetAll();
if (users.Any(m => m.UserName.ToLower() == username.ToLower()))
{
status = MembershipCreateStatus.DuplicateUserName;
}
else if (users.Any(m => m.EmailAddress.ToLower() == email.ToLower()))
{
status = MembershipCreateStatus.DuplicateEmail;
}
else
{
byte[] salt = new byte[24];
byte[] hash = calculateHash(password, ref salt);
RedisMember rm = new RedisMember()
{
Id = Guid.NewGuid(),
UserName = username,
PassHash = hash,
PassSalt = salt,
EmailAddress = email,
IsApproved = false,
IsLockedOut = false,
CreationDate = DateTime.Now,
LastLoginDate = DateTime.MinValue,
LastActivityDate = DateTime.MinValue,
LastLockoutDate = DateTime.MinValue
};
client.Store(rm);
status = MembershipCreateStatus.Success;
mu = new MembershipUser(this.Name, rm.UserName, rm.Id, rm.EmailAddress, "", "", rm.IsApproved, rm.IsLockedOut, rm.CreationDate, rm.LastLoginDate, rm.LastActivityDate, DateTime.MinValue, rm.LastLockoutDate);
}
}
return mu;
}
private static byte[] calculateHash(string password, ref byte[] salt)
{
if (!salt.Any(v => v != 0))
{
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
rng.GetBytes(salt);
}
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
byte[] hashPlaintext = new byte[salt.Length + passwordBytes.Length];
passwordBytes.CopyTo(hashPlaintext, 0);
salt.CopyTo(hashPlaintext, passwordBytes.Length);
SHA512CryptoServiceProvider sha = new SHA512CryptoServiceProvider();
byte[] hash = sha.ComputeHash(hashPlaintext);
return hash;
}
public override bool DeleteUser(string username, bool deleteAllRelatedData)
{
using (RedisClient rClient = new RedisClient(DatabaseConfig.Host, DatabaseConfig.Port, db: DatabaseConfig.Database))
{
var client = rClient.As<RedisMember>();
var user = client.GetAll().SingleOrDefault(m => m.UserName.ToLower() == username.ToLower());
if (user != null)
{
client.DeleteById(user.Id);
return true;
}
else
{
return false;
}
}
}
public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
{
throw new NotImplementedException();
}
public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
{
throw new NotImplementedException();
}
public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
{
MembershipUserCollection muc = new MembershipUserCollection();
using (RedisClient rClient = new RedisClient(DatabaseConfig.Host, DatabaseConfig.Port, db: DatabaseConfig.Database))
{
var client = rClient.As<RedisMember>();
var users = client.GetAll();
totalRecords = users.Count;
var pageItems = users.Skip(pageIndex * pageSize).Take(pageSize);
foreach (var rm in pageItems)
{
muc.Add(new MembershipUser(this.Name, rm.UserName, rm.Id, rm.EmailAddress, "", "", rm.IsApproved, rm.IsLockedOut, rm.CreationDate, rm.LastLoginDate, rm.LastActivityDate, DateTime.MinValue, rm.LastLockoutDate));
}
}
return muc;
}
public override int GetNumberOfUsersOnline()
{
throw new NotImplementedException();
}
public override string GetPassword(string username, string answer)
{
throw new NotImplementedException();
}
public override MembershipUser GetUser(string username, bool userIsOnline)
{
using (RedisClient rClient = new RedisClient(DatabaseConfig.Host, DatabaseConfig.Port, db: DatabaseConfig.Database))
{
var client = rClient.As<RedisMember>();
var rm = client.GetAll().SingleOrDefault(m => m.UserName.ToLower() == username.ToLower());
if (rm == null)
{
return null;
}
else
{
return new MembershipUser(this.Name, rm.UserName, rm.Id, rm.EmailAddress, "", "", rm.IsApproved, rm.IsLockedOut, rm.CreationDate, rm.LastLoginDate, rm.LastActivityDate, DateTime.MinValue, rm.LastLockoutDate);
}
}
}
public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
{
using (RedisClient rClient = new RedisClient(DatabaseConfig.Host, DatabaseConfig.Port, db: DatabaseConfig.Database))
{
var client = rClient.As<RedisMember>();
var rm = client.GetById(providerUserKey);
if (rm == null)
{
return null;
}
else
{
return new MembershipUser(this.Name, rm.UserName, rm.Id, rm.EmailAddress, "", "", rm.IsApproved, rm.IsLockedOut, rm.CreationDate, rm.LastLoginDate, rm.LastActivityDate, DateTime.MinValue, rm.LastLockoutDate);
}
}
}
public override string GetUserNameByEmail(string email)
{
using (RedisClient rClient = new RedisClient(DatabaseConfig.Host, DatabaseConfig.Port, db: DatabaseConfig.Database))
{
var client = rClient.As<RedisMember>();
var rm = client.GetAll().SingleOrDefault(m => m.EmailAddress.ToLower() == email.ToLower());
if (rm == null)
{
return "";
}
else
{
return rm.UserName;
}
}
}
public override string ResetPassword(string username, string answer)
{
throw new NotImplementedException();
}
public void ChangeApproval(Guid Id, bool newStatus)
{
using (RedisClient rClient = new RedisClient(DatabaseConfig.Host, DatabaseConfig.Port, db: DatabaseConfig.Database))
{
var client = rClient.As<RedisMember>();
var rm = client.GetById(Id);
if (rm != null)
{
rm.IsApproved = newStatus;
client.Store(rm);
}
}
}
public void ChangeLockStatus(Guid Id, bool newStatus)
{
using (RedisClient rClient = new RedisClient(DatabaseConfig.Host, DatabaseConfig.Port, db: DatabaseConfig.Database))
{
var client = rClient.As<RedisMember>();
var rm = client.GetById(Id);
if (rm != null)
{
rm.IsLockedOut = newStatus;
if (newStatus)
{
rm.LastLockoutDate = DateTime.Now;
}
else
{
rm.LockoutWindowAttempts = 0;
rm.LockoutWindowStart = DateTime.MinValue;
}
client.Store(rm);
}
}
}
public override bool UnlockUser(string userName)
{
using (RedisClient rClient = new RedisClient(DatabaseConfig.Host, DatabaseConfig.Port, db: DatabaseConfig.Database))
{
var client = rClient.As<RedisMember>();
var rm = client.GetAll().SingleOrDefault(m => m.UserName.ToLower() == userName.ToLower());
rm.IsLockedOut = false;
client.Store(rm);
return true;
}
}
public override void UpdateUser(MembershipUser user)
{
throw new NotImplementedException();
}
public override bool ValidateUser(string username, string password)
{
using (RedisClient rClient = new RedisClient(DatabaseConfig.Host, DatabaseConfig.Port, db: DatabaseConfig.Database))
{
var client = rClient.As<RedisMember>();
var rm = client.GetAll().SingleOrDefault(m => m.UserName.ToLower() == username.ToLower());
if (rm == null || !(rm.IsApproved && !rm.IsLockedOut))
{
return false;
}
byte[] salt = rm.PassSalt;
byte[] hash = calculateHash(password, ref salt);
bool isFail = false;
for (int i = 0; i > hash.Length; i++)
{
isFail |= (hash[i] != rm.PassHash[i]);
}
if (isFail)
{
if (rm.LockoutWindowStart == DateTime.MinValue)
{
rm.LockoutWindowStart = DateTime.Now;
rm.LockoutWindowAttempts = 1;
}
else
{
if (rm.LockoutWindowStart.AddMinutes(PasswordAttemptWindow) > DateTime.Now)
{
// still within window
rm.LockoutWindowAttempts++;
if (rm.LockoutWindowAttempts >= MaxInvalidPasswordAttempts)
{
rm.IsLockedOut = true;
}
}
else
{
// outside of window, reset
rm.LockoutWindowStart = DateTime.Now;
rm.LockoutWindowAttempts = 1;
}
}
}
else
{
rm.LastLoginDate = DateTime.Now;
rm.LockoutWindowStart = DateTime.MinValue;
rm.LockoutWindowAttempts = 0;
}
client.Store(rm);
return !isFail;
}
}
private static bool tryReadBool(string config, bool defaultValue)
{
bool temp = false;
bool success = bool.TryParse(config, out temp);
return success ? temp : defaultValue;
}
private static int tryReadInt(string config, int defaultValue)
{
int temp = 0;
bool success = int.TryParse(config, out temp);
return success ? temp : defaultValue;
}
}
[DataObject]
public class RedisMember : IHasId<Guid>
{
[Key]
[Index]
public Guid Id { get; set; }
[@Required]
[DisplayName("Username")]
[Key]
public string UserName { get; set; }
[@Required]
[MaxLength(512)]
public byte[] PassHash { get; set; }
[@Required]
[MaxLength(24)]
public byte[] PassSalt { get; set; }
[@Required]
[DisplayName("Email Address")]
[Key]
public string EmailAddress { get; set; }
public bool IsApproved { get; set; }
public bool IsLockedOut { get; set; }
public DateTime CreationDate { get; set; }
public DateTime LastActivityDate { get; set; }
public DateTime LastLockoutDate { get; set; }
public DateTime LastLoginDate { get; set; }
public DateTime LockoutWindowStart { get; set; }
public int LockoutWindowAttempts { get; set; }
}
}

View File

@ -1,244 +0,0 @@
using NServiceKit.DataAnnotations;
using NServiceKit.DesignPatterns.Model;
using NServiceKit.Redis;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web.Security;
using Required = System.ComponentModel.DataAnnotations.RequiredAttribute;
namespace BuildFeed.Auth
{
public class RedisRoleProvider : RoleProvider
{
public override string ApplicationName
{
get { return ""; }
set { }
}
public override void AddUsersToRoles(string[] usernames, string[] roleNames)
{
using (RedisClient rClient = new RedisClient(DatabaseConfig.Host, DatabaseConfig.Port, db: DatabaseConfig.Database))
{
var client = rClient.As<RedisRole>();
var uClient = rClient.As<RedisMember>();
List<RedisRole> roles = new List<RedisRole>();
roles.AddRange(from r in client.GetAll()
where roleNames.Any(n => n == r.RoleName)
select r);
List<RedisMember> users = new List<RedisMember>();
users.AddRange(from u in uClient.GetAll()
where usernames.Any(n => n == u.UserName)
select u);
for (int i = 0; i < roles.Count; i++)
{
List<Guid> newUsers = new List<Guid>();
if(roles[i].Users != null)
{
var usersToAdd = from u in users
where !roles[i].Users.Any(v => v == u.Id)
select u.Id;
newUsers.AddRange(roles[i].Users);
newUsers.AddRange(usersToAdd);
}
roles[i].Users = newUsers.ToArray();
}
client.StoreAll(roles);
}
}
public override void CreateRole(string roleName)
{
using (RedisClient rClient = new RedisClient(DatabaseConfig.Host, DatabaseConfig.Port, db: DatabaseConfig.Database))
{
var client = rClient.As<RedisRole>();
RedisRole rr = new RedisRole()
{
Id = Guid.NewGuid(),
RoleName = roleName
};
client.Store(rr);
}
}
public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
{
using (RedisClient rClient = new RedisClient(DatabaseConfig.Host, DatabaseConfig.Port, db: DatabaseConfig.Database))
{
var client = rClient.As<RedisRole>();
var role = client.GetAll().SingleOrDefault(r => r.RoleName == roleName);
if (role.Users.Length > 0 && throwOnPopulatedRole)
{
throw new Exception("This role still has users");
}
client.DeleteById(role.Id);
return true;
}
}
public override string[] FindUsersInRole(string roleName, string usernameToMatch)
{
using (RedisClient rClient = new RedisClient(DatabaseConfig.Host, DatabaseConfig.Port, db: DatabaseConfig.Database))
{
var client = rClient.As<RedisRole>();
var uClient = rClient.As<RedisMember>();
var userIds = from r in client.GetAll()
where r.RoleName == roleName
from u in r.Users
select u;
var users = uClient.GetByIds(userIds);
return (from u in users
where u.UserName.ToLower().Contains(usernameToMatch.ToLower())
select u.UserName).ToArray();
}
}
public override string[] GetAllRoles()
{
using (RedisClient rClient = new RedisClient(DatabaseConfig.Host, DatabaseConfig.Port, db: DatabaseConfig.Database))
{
var client = rClient.As<RedisRole>();
return (from r in client.GetAll()
select r.RoleName).ToArray();
}
}
public override string[] GetRolesForUser(string username)
{
using (RedisClient rClient = new RedisClient(DatabaseConfig.Host, DatabaseConfig.Port, db: DatabaseConfig.Database))
{
var client = rClient.As<RedisRole>();
var uClient = rClient.As<RedisMember>();
var user = uClient.GetAll().SingleOrDefault(u => u.UserName.ToLower() == username.ToLower());
if (user == null)
{
throw new Exception("Username does not exist");
}
return (from r in client.GetAll()
where r.Users != null
where r.Users.Any(u => u == user.Id)
select r.RoleName).ToArray();
}
}
public override string[] GetUsersInRole(string roleName)
{
using (RedisClient rClient = new RedisClient(DatabaseConfig.Host, DatabaseConfig.Port, db: DatabaseConfig.Database))
{
var client = rClient.As<RedisRole>();
var uClient = rClient.As<RedisMember>();
var userIds = from r in client.GetAll()
where r.RoleName == roleName
from u in r.Users
select u;
var users = uClient.GetByIds(userIds);
return (from u in users
select u.UserName).ToArray();
}
}
public override bool IsUserInRole(string username, string roleName)
{
using (RedisClient rClient = new RedisClient(DatabaseConfig.Host, DatabaseConfig.Port, db: DatabaseConfig.Database))
{
var client = rClient.As<RedisRole>();
var uClient = rClient.As<RedisMember>();
var user = uClient.GetAll().SingleOrDefault(u => u.UserName.ToLower() == username.ToLower());
if (user == null)
{
throw new Exception();
}
var role = client.GetAll().SingleOrDefault(r => r.RoleName == roleName);
if(role.Users == null)
{
return false;
}
return role.Users.Any(u => u == user.Id);
}
}
public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
{
using (RedisClient rClient = new RedisClient(DatabaseConfig.Host, DatabaseConfig.Port, db: DatabaseConfig.Database))
{
var client = rClient.As<RedisRole>();
var uClient = rClient.As<RedisMember>();
List<RedisRole> roles = new List<RedisRole>();
roles.AddRange(from r in client.GetAll()
where roleNames.Any(n => n == r.RoleName)
select r);
List<RedisMember> users = new List<RedisMember>();
users.AddRange(from u in uClient.GetAll()
where usernames.Any(n => n == u.UserName)
select u);
for (int i = 0; i < roles.Count; i++)
{
roles[i].Users = (from u in roles[i].Users
where !users.Any(v => v.Id == u)
select u).ToArray();
}
client.StoreAll(roles);
}
}
public override bool RoleExists(string roleName)
{
using (RedisClient rClient = new RedisClient(DatabaseConfig.Host, DatabaseConfig.Port, db: DatabaseConfig.Database))
{
var client = rClient.As<RedisRole>();
return client.GetAll().Any(r => r.RoleName == roleName);
}
}
}
[DataObject]
public class RedisRole : IHasId<Guid>
{
[Key]
[Index]
public Guid Id { get; set; }
[@Required]
[DisplayName("Role name")]
[Key]
public string RoleName { get; set; }
public Guid[] Users { get; set; }
}
}

View File

@ -4,6 +4,7 @@
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.UI;
namespace BuildFeed.Controllers
{
@ -12,7 +13,9 @@ public class frontController : Controller
private const int _pageSize = 25;
[Route("")]
#if !DEBUG
[OutputCache(Duration = 600, VaryByParam = "none")]
#endif
public ActionResult index()
{
var buildGroups = from b in Build.Select()
@ -30,5 +33,19 @@ bg.Key.Revision descending
select bg;
return View(buildGroups);
}
[Route("group/{major}.{minor}.{number}.{revision}")]
public ActionResult viewGroup(byte major, byte minor, ushort number, ushort? revision = null)
{
var builds = from b in Build.Select()
where b.MajorVersion == major
where b.MinorVersion == minor
where b.Number == number
where b.Revision == revision
orderby b.BuildTime descending
select b;
return PartialView(builds);
}
}
}

View File

@ -12,8 +12,7 @@ namespace BuildFeed.Controllers
{
public class rssController : Controller
{
//
// GET: /rss/
[Route("rss/compiled")]
public async Task<ActionResult> index()
{
var builds = Build.SelectInBuildOrder().Take(20);
@ -46,6 +45,7 @@ public async Task<ActionResult> index()
return new EmptyResult();
}
[Route("rss/added")]
public async Task<ActionResult> added()
{
var builds = Build.Select().OrderByDescending(b => b.Added).Take(20);
@ -79,6 +79,7 @@ public async Task<ActionResult> added()
return new EmptyResult();
}
[Route("rss/leaked")]
public async Task<ActionResult> leaked()
{
var builds = Build.Select().Where(b => b.LeakDate.HasValue).OrderByDescending(b => b.LeakDate.Value).Take(20);
@ -112,7 +113,7 @@ public async Task<ActionResult> leaked()
return new EmptyResult();
}
[Route("rss/version")]
public async Task<ActionResult> version()
{
var builds = Build.SelectInVersionOrder()
@ -147,7 +148,7 @@ public async Task<ActionResult> version()
return new EmptyResult();
}
[Route("rss/flight/{id}")]
public async Task<ActionResult> flight(LevelOfFlight id)
{
var builds = Build.SelectInBuildOrder()

View File

@ -17,18 +17,14 @@ namespace BuildFeed.Controllers
{
public class supportController : Controller
{
// GET: support
public ActionResult index()
{
return View();
}
[Route("login")]
public ActionResult login()
{
return View();
}
[HttpPost]
[Route("login")]
public ActionResult login(LoginUser ru)
{
if (ModelState.IsValid)
@ -56,12 +52,14 @@ public ActionResult login(LoginUser ru)
}
[Authorize]
[Route("password")]
public ActionResult password()
{
return View();
}
[HttpPost]
[Route("password")]
public ActionResult password(ChangePassword cp)
{
if (ModelState.IsValid)
@ -79,18 +77,21 @@ public ActionResult password(ChangePassword cp)
return View(cp);
}
[Route("logout")]
public ActionResult logout()
{
FormsAuthentication.SignOut();
return Redirect("/");
}
[Route("register")]
public ActionResult register()
{
return View();
}
[HttpPost]
[Route("register")]
public ActionResult register(RegistrationUser ru)
{
if (ModelState.IsValid)
@ -120,6 +121,7 @@ public ActionResult register(RegistrationUser ru)
return View(ru);
}
[Route("register")]
public ActionResult thanks_register()
{
return View();
@ -162,13 +164,16 @@ public async Task<ActionResult> question(QuestionForm qf)
}
}
[Route("rss")]
public ActionResult rss()
{
return View();
}
[Route("support/sitemap")]
[Route("sitemap")]
#if !DEBUG
[OutputCache(Duration = 3600, VaryByParam = "none")]
#endif
public ActionResult sitemap()
{
IEnumerable<Build> builds = Build.SelectInVersionOrder();
@ -267,8 +272,10 @@ orderby bv.Key
return View(model);
}
[Route("support/xmlsitemap")]
[Route("xml-sitemap")]
#if !DEBUG
[OutputCache(Duration = 3600, VaryByParam = "none")]
#endif
public ActionResult xmlsitemap()
{
XNamespace xn = XNamespace.Get("http://www.sitemaps.org/schemas/sitemap/0.9");
@ -303,8 +310,10 @@ public ActionResult xmlsitemap()
return new EmptyResult();
}
[Route("support/stats")]
[Route("statistics")]
#if !DEBUG
[OutputCache(Duration = 3600, VaryByParam = "none")]
#endif
public ActionResult stats()
{
var builds = Build.Select();

View File

@ -66,26 +66,6 @@ public class Build : IHasId<long>
[DisplayFormat(ConvertEmptyStringToNull = true, ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")]
public DateTime? LeakDate { get; set; }
[RegularExpression("http://betawiki\\.net/.+")]
[DisplayName("BetaWiki (Client)")]
public Uri BetaWikiUri { get; set; }
[RegularExpression("http://betawiki\\.net/.+")]
[DisplayName("BetaWiki (Server)")]
public Uri BetaWikiServerUri { get; set; }
[RegularExpression("http://www\\.betaarchive\\.com/wiki/.+")]
[DisplayName("BetaArchive Wiki")]
public Uri BetaArchiveUri { get; set; }
[RegularExpression("http://longhorn\\.ms/.+")]
[DisplayName("Longhorn.ms")]
public Uri LonghornMsUri { get; set; }
[RegularExpression("https://winworldpc\\.com/.+")]
[DisplayName("WinWorldPC Library")]
public Uri WinWorldPCUri { get; set; }
[DisplayName("Flight Level")]
[EnumDataType(typeof(LevelOfFlight))]
public LevelOfFlight FlightLevel { get; set; }

1
Scripts/index-page.ts Normal file
View File

@ -0,0 +1 @@


3148
Scripts/typings/jquery/jquery.d.ts vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -7,13 +7,13 @@
@foreach (var group in Model)
{
<div class="col-sm-2">
<h3>@group.Key.ToString()</h3>
<h3 class="build-group-title"><a href="#"><i class="fa fa-plus-square"></i></a> @group.Key.ToString()</h3>
<p>
@if(group.Any(k => k.BuildTime.HasValue))
{
<span><i class="fa fa-calendar fa-fw"></i> @group.Max(k => k.BuildTime).Value.ToString("dd MMMM yyyy")</span><br />
}
<i class="fa fa-server fa-fw"></i> @group.Count().ToString() variants
<i class="fa fa-server fa-fw"></i> @group.Count().ToString() builds
</p>
</div>
}

View File

@ -0,0 +1,11 @@
@model IEnumerable<BuildFeed.Models.Build>
@{
Layout = null;
}
<ul>
@foreach (var build in Model)
{
<li>@build.FullBuildString</li>
}
</ul>

View File

@ -48,9 +48,9 @@
<div class="container">
@Html.ActionLink("BuildFeed", "index", new { controller = "front", area = "" }, new { @class = "navbar-brand" })
<ul class="nav navbar-nav navbar-right">
<li><a href="https://twitter.com/buildfeed" title="Twitter" target="_blank"><i class="fa fa-twitter"></i> Twitter</a></li>
<li><a href="@Url.Action("stats", new { controller = "support", area = "" }) " title="Statistics"><i class="fa fa-line-chart"></i> Statistics</a></li>
<li><a href="@Url.Action("rss", new { controller = "support", area = "" })" title="RSS Feeds"><i class="fa fa-rss"></i> RSS Feeds</a></li>
<li><a href="https://twitter.com/buildfeed" title="Twitter" target="_blank"><i class="fa fa-twitter"></i> Twitter</a></li>
</ul>
</div>
</div>

View File

@ -1,42 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<!--
For more information on how to configure your ASP.NET application, please visit
http://go.microsoft.com/fwlink/?LinkId=301880
-->
<configuration>
<appSettings>
<add key="webpages:Version" value="3.0.0.0"/>
<add key="webpages:Enabled" value="false"/>
<add key="PreserveLoginUrl" value="true"/>
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
<add key="data:ServerHost" value="localhost"/>
<add key="data:ServerPort" value="6379"/>
<add key="data:ServerDB" value="1"/>
<add key="form:QuestionFromEmail" value="smtp@betawiki.net"/>
<add key="form:QuestionToEmail" value="thomas@buildfeed.net"/>
<add key="RouteDebugger:Enabled" value="false"/>
<add key="webpages:Version" value="3.0.0.0" />
<add key="webpages:Enabled" value="false" />
<add key="PreserveLoginUrl" value="true" />
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
<add key="data:ServerHost" value="localhost" />
<add key="data:ServerPort" value="6379" />
<add key="data:ServerDB" value="1" />
<add key="form:QuestionFromEmail" value="smtp@betawiki.net" />
<add key="form:QuestionToEmail" value="thomas@buildfeed.net" />
<add key="RouteDebugger:Enabled" value="false" />
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.5"/>
<httpRuntime targetFramework="4.5"/>
<globalization culture="en-gb" uiCulture="en-gb"/>
<trace enabled="false" requestLimit="40" pageOutput="true"/>
<sessionState cookieless="UseCookies"/>
<anonymousIdentification cookieless="UseCookies" enabled="false"/>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
<globalization culture="en-gb" uiCulture="en-gb" />
<trace enabled="false" requestLimit="40" pageOutput="true" />
<sessionState cookieless="UseCookies" />
<anonymousIdentification cookieless="UseCookies" enabled="false" />
<authentication mode="Forms">
<forms loginUrl="/support/login/" cookieless="UseCookies" name="BuildFeedAuth"/>
<forms loginUrl="/login/" cookieless="UseCookies" name="BuildFeedAuth" />
</authentication>
<membership defaultProvider="BuildFeedMemberProvider">
<providers>
<clear/>
<add name="BuildFeedMemberProvider" type="BuildFeed.Auth.RedisMembershipProvider"/>
<clear />
<add name="BuildFeedMemberProvider" type="RedisAuth.RedisMembershipProvider,RedisAuth" />
</providers>
</membership>
<roleManager defaultProvider="BuildFeedRoleProvider" enabled="true">
<providers>
<clear/>
<add name="BuildFeedRoleProvider" type="BuildFeed.Auth.RedisRoleProvider"/>
<clear />
<add name="BuildFeedRoleProvider" type="RedisAuth.RedisRoleProvider,RedisAuth" />
</providers>
</roleManager>
<httpModules>
@ -45,57 +45,57 @@
<system.webServer>
<modules>
</modules>
<urlCompression doDynamicCompression="true"/>
<validation validateIntegratedModeConfiguration="false"/>
<urlCompression doDynamicCompression="true" />
<validation validateIntegratedModeConfiguration="false" />
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0"/>
<remove name="OPTIONSVerbHandler"/>
<remove name="TRACEVerbHandler"/>
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0"/>
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="OPTIONSVerbHandler" />
<remove name="TRACEVerbHandler" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
<system.net>
<mailSettings>
<smtp configSource="smtp.config"/>
<smtp configSource="smtp.config" />
</mailSettings>
</system.net>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.Diagnostics.Tracing.EventSource" publicKeyToken="B03F5F7F11D50A3A" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-1.1.20.0" newVersion="1.1.20.0"/>
<assemblyIdentity name="Microsoft.Diagnostics.Tracing.EventSource" publicKeyToken="B03F5F7F11D50A3A" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.1.20.0" newVersion="1.1.20.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" culture="neutral" publicKeyToken="30ad4fe6b2a6aeed"/>
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0"/>
<assemblyIdentity name="Newtonsoft.Json" culture="neutral" publicKeyToken="30ad4fe6b2a6aeed" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Optimization" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="1.1.0.0"/>
<assemblyIdentity name="System.Web.Optimization" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="1.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="0.0.0.0-1.6.5135.21930" newVersion="1.6.5135.21930"/>
<assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="0.0.0.0-1.6.5135.21930" newVersion="1.6.5135.21930" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Antlr3.Runtime" publicKeyToken="eb42632606e9261f" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-3.5.0.2" newVersion="3.5.0.2"/>
<assemblyIdentity name="Antlr3.Runtime" publicKeyToken="eb42632606e9261f" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.5.0.2" newVersion="3.5.0.2" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Diagnostics.Tracing.EventSource" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-1.1.14.0" newVersion="1.1.14.0"/>
<assemblyIdentity name="Microsoft.Diagnostics.Tracing.EventSource" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.1.20.0" newVersion="1.1.20.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0"/>
<assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0"/>
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-5.2.3.0" newVersion="5.2.3.0"/>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>

View File

@ -20,6 +20,19 @@ h1
padding-top: 70px;
}
.build-group-title
{
font-size: 22px;
}
.build-group-title a
{
font-size: 0.6em;
vertical-align: 3px;
display: inline-block;
margin-left: 0.3em;
}
.build-head
{
margin-bottom: 0.33em;

View File

@ -1,33 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Antlr" version="3.5.0.2" targetFramework="net45" />
<package id="Chart.js" version="1.0.1" targetFramework="net45" />
<package id="jQuery" version="2.1.3" targetFramework="net45" />
<package id="jQuery.Validation" version="1.13.1" targetFramework="net45" />
<package id="Microsoft.ApplicationInsights" version="0.13.2-build00132" targetFramework="net45" />
<package id="Microsoft.ApplicationInsights.PerformanceCollector" version="0.13.2-build00132" targetFramework="net45" />
<package id="Microsoft.ApplicationInsights.RuntimeTelemetry" version="0.13.2-build00132" targetFramework="net45" />
<package id="Microsoft.ApplicationInsights.Web" version="0.13.2-build00132" targetFramework="net45" />
<package id="Microsoft.AspNet.Mvc" version="5.2.3" targetFramework="net45" />
<package id="Microsoft.AspNet.Razor" version="3.2.3" targetFramework="net45" />
<package id="Microsoft.AspNet.Web.Optimization" version="1.1.3" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi" version="5.2.3" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.WebHost" version="5.2.3" targetFramework="net45" />
<package id="Microsoft.AspNet.WebPages" version="3.2.3" targetFramework="net45" />
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="net45" />
<package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net45" />
<package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net45" />
<package id="Microsoft.Diagnostics.Instrumentation.Extensions.Intercept" version="0.13.0-build22724" targetFramework="net45" />
<package id="Microsoft.Diagnostics.Tracing.EventSource.Redist" version="1.1.20-beta" targetFramework="net45" />
<package id="Microsoft.jQuery.Unobtrusive.Validation" version="3.2.3" targetFramework="net45" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net45" />
<package id="Newtonsoft.Json" version="6.0.8" targetFramework="net45" />
<package id="NServiceKit.Common" version="1.0.27" targetFramework="net45" />
<package id="NServiceKit.Redis" version="1.0.10" targetFramework="net45" />
<package id="NServiceKit.Text" version="1.0.10" targetFramework="net45" />
<package id="routedebugger" version="2.1.4.0" targetFramework="net45" />
<package id="WebGrease" version="1.6.0" targetFramework="net45" />
<package id="xwebrss" version="1.2.1.130" targetFramework="net45" />
<package id="Antlr" version="3.5.0.2" targetFramework="net45" userInstalled="true" />
<package id="Chart.js" version="1.0.1" targetFramework="net45" userInstalled="true" />
<package id="jQuery" version="2.1.3" targetFramework="net45" userInstalled="true" />
<package id="jquery.TypeScript.DefinitelyTyped" version="2.2.2" targetFramework="net45" userInstalled="true" />
<package id="jQuery.Validation" version="1.13.1" targetFramework="net45" userInstalled="true" />
<package id="Microsoft.ApplicationInsights" version="0.13.2-build00132" targetFramework="net45" userInstalled="true" />
<package id="Microsoft.ApplicationInsights.PerformanceCollector" version="0.13.2-build00132" targetFramework="net45" userInstalled="true" />
<package id="Microsoft.ApplicationInsights.RuntimeTelemetry" version="0.13.2-build00132" targetFramework="net45" userInstalled="true" />
<package id="Microsoft.ApplicationInsights.Web" version="0.13.2-build00132" targetFramework="net45" userInstalled="true" />
<package id="Microsoft.AspNet.Mvc" version="5.2.3" targetFramework="net45" userInstalled="true" />
<package id="Microsoft.AspNet.Razor" version="3.2.3" targetFramework="net45" userInstalled="true" />
<package id="Microsoft.AspNet.Web.Optimization" version="1.1.3" targetFramework="net45" userInstalled="true" />
<package id="Microsoft.AspNet.WebApi" version="5.2.3" targetFramework="net45" userInstalled="true" />
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net45" userInstalled="true" />
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net45" userInstalled="true" />
<package id="Microsoft.AspNet.WebApi.WebHost" version="5.2.3" targetFramework="net45" userInstalled="true" />
<package id="Microsoft.AspNet.WebPages" version="3.2.3" targetFramework="net45" userInstalled="true" />
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="net45" userInstalled="true" />
<package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net45" userInstalled="true" />
<package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net45" userInstalled="true" />
<package id="Microsoft.Diagnostics.Instrumentation.Extensions.Intercept" version="0.13.0-build22724" targetFramework="net45" userInstalled="true" />
<package id="Microsoft.Diagnostics.Tracing.EventSource.Redist" version="1.1.20-beta" targetFramework="net45" userInstalled="true" />
<package id="Microsoft.jQuery.Unobtrusive.Validation" version="3.2.3" targetFramework="net45" userInstalled="true" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net45" userInstalled="true" />
<package id="Newtonsoft.Json" version="6.0.8" targetFramework="net45" userInstalled="true" />
<package id="NServiceKit.Common" version="1.0.27" targetFramework="net45" userInstalled="true" />
<package id="NServiceKit.Redis" version="1.0.10" targetFramework="net45" userInstalled="true" />
<package id="NServiceKit.Text" version="1.0.10" targetFramework="net45" userInstalled="true" />
<package id="routedebugger" version="2.1.4.0" targetFramework="net45" userInstalled="true" />
<package id="WebGrease" version="1.6.0" targetFramework="net45" userInstalled="true" />
<package id="xwebrss" version="1.2.1.130" targetFramework="net45" userInstalled="true" />
</packages>