diff --git a/.vs/config/applicationhost.config b/.vs/config/applicationhost.config index cab06bd..9c66883 100644 --- a/.vs/config/applicationhost.config +++ b/.vs/config/applicationhost.config @@ -171,7 +171,7 @@ - + diff --git a/BuildFeed.sln b/BuildFeed.sln index 6c40a66..4a594ff 100644 --- a/BuildFeed.sln +++ b/BuildFeed.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.22609.0 +VisualStudioVersion = 14.0.23107.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BuildFeed", "BuildFeed\BuildFeed.csproj", "{CDDCF754-ECAA-4A66-ADAA-62957A57A51B}" EndProject @@ -12,20 +12,94 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{5AA81F EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RedisAuth", "RedisAuth\RedisAuth.csproj", "{C6A16CF0-41DA-4B90-918A-CB84A8C3F1E2}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MongoAuth", "MongoAuth\MongoAuth.csproj", "{7C67BFB9-1B3B-4676-A58D-10573DA82CFE}" +EndProject +Project("{262852C6-CD72-467D-83FE-5EEB1973A190}") = "BuildFeedApp-Westminster", "BuildFeedApp-Westminster\BuildFeedApp-Westminster.jsproj", "{5CAADB66-1FC2-4492-B766-36354687120D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|ARM = Debug|ARM + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|ARM = Release|ARM + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {CDDCF754-ECAA-4A66-ADAA-62957A57A51B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CDDCF754-ECAA-4A66-ADAA-62957A57A51B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CDDCF754-ECAA-4A66-ADAA-62957A57A51B}.Debug|ARM.ActiveCfg = Debug|Any CPU + {CDDCF754-ECAA-4A66-ADAA-62957A57A51B}.Debug|ARM.Build.0 = Debug|Any CPU + {CDDCF754-ECAA-4A66-ADAA-62957A57A51B}.Debug|x64.ActiveCfg = Debug|Any CPU + {CDDCF754-ECAA-4A66-ADAA-62957A57A51B}.Debug|x64.Build.0 = Debug|Any CPU + {CDDCF754-ECAA-4A66-ADAA-62957A57A51B}.Debug|x86.ActiveCfg = Debug|Any CPU + {CDDCF754-ECAA-4A66-ADAA-62957A57A51B}.Debug|x86.Build.0 = Debug|Any CPU {CDDCF754-ECAA-4A66-ADAA-62957A57A51B}.Release|Any CPU.ActiveCfg = Release|Any CPU {CDDCF754-ECAA-4A66-ADAA-62957A57A51B}.Release|Any CPU.Build.0 = Release|Any CPU + {CDDCF754-ECAA-4A66-ADAA-62957A57A51B}.Release|ARM.ActiveCfg = Release|Any CPU + {CDDCF754-ECAA-4A66-ADAA-62957A57A51B}.Release|ARM.Build.0 = Release|Any CPU + {CDDCF754-ECAA-4A66-ADAA-62957A57A51B}.Release|x64.ActiveCfg = Release|Any CPU + {CDDCF754-ECAA-4A66-ADAA-62957A57A51B}.Release|x64.Build.0 = Release|Any CPU + {CDDCF754-ECAA-4A66-ADAA-62957A57A51B}.Release|x86.ActiveCfg = Release|Any CPU + {CDDCF754-ECAA-4A66-ADAA-62957A57A51B}.Release|x86.Build.0 = Release|Any CPU {C6A16CF0-41DA-4B90-918A-CB84A8C3F1E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C6A16CF0-41DA-4B90-918A-CB84A8C3F1E2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C6A16CF0-41DA-4B90-918A-CB84A8C3F1E2}.Debug|ARM.ActiveCfg = Debug|Any CPU + {C6A16CF0-41DA-4B90-918A-CB84A8C3F1E2}.Debug|ARM.Build.0 = Debug|Any CPU + {C6A16CF0-41DA-4B90-918A-CB84A8C3F1E2}.Debug|x64.ActiveCfg = Debug|Any CPU + {C6A16CF0-41DA-4B90-918A-CB84A8C3F1E2}.Debug|x64.Build.0 = Debug|Any CPU + {C6A16CF0-41DA-4B90-918A-CB84A8C3F1E2}.Debug|x86.ActiveCfg = Debug|Any CPU + {C6A16CF0-41DA-4B90-918A-CB84A8C3F1E2}.Debug|x86.Build.0 = Debug|Any CPU {C6A16CF0-41DA-4B90-918A-CB84A8C3F1E2}.Release|Any CPU.ActiveCfg = Release|Any CPU {C6A16CF0-41DA-4B90-918A-CB84A8C3F1E2}.Release|Any CPU.Build.0 = Release|Any CPU + {C6A16CF0-41DA-4B90-918A-CB84A8C3F1E2}.Release|ARM.ActiveCfg = Release|Any CPU + {C6A16CF0-41DA-4B90-918A-CB84A8C3F1E2}.Release|ARM.Build.0 = Release|Any CPU + {C6A16CF0-41DA-4B90-918A-CB84A8C3F1E2}.Release|x64.ActiveCfg = Release|Any CPU + {C6A16CF0-41DA-4B90-918A-CB84A8C3F1E2}.Release|x64.Build.0 = Release|Any CPU + {C6A16CF0-41DA-4B90-918A-CB84A8C3F1E2}.Release|x86.ActiveCfg = Release|Any CPU + {C6A16CF0-41DA-4B90-918A-CB84A8C3F1E2}.Release|x86.Build.0 = Release|Any CPU + {7C67BFB9-1B3B-4676-A58D-10573DA82CFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7C67BFB9-1B3B-4676-A58D-10573DA82CFE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7C67BFB9-1B3B-4676-A58D-10573DA82CFE}.Debug|ARM.ActiveCfg = Debug|Any CPU + {7C67BFB9-1B3B-4676-A58D-10573DA82CFE}.Debug|ARM.Build.0 = Debug|Any CPU + {7C67BFB9-1B3B-4676-A58D-10573DA82CFE}.Debug|x64.ActiveCfg = Debug|Any CPU + {7C67BFB9-1B3B-4676-A58D-10573DA82CFE}.Debug|x64.Build.0 = Debug|Any CPU + {7C67BFB9-1B3B-4676-A58D-10573DA82CFE}.Debug|x86.ActiveCfg = Debug|Any CPU + {7C67BFB9-1B3B-4676-A58D-10573DA82CFE}.Debug|x86.Build.0 = Debug|Any CPU + {7C67BFB9-1B3B-4676-A58D-10573DA82CFE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7C67BFB9-1B3B-4676-A58D-10573DA82CFE}.Release|Any CPU.Build.0 = Release|Any CPU + {7C67BFB9-1B3B-4676-A58D-10573DA82CFE}.Release|ARM.ActiveCfg = Release|Any CPU + {7C67BFB9-1B3B-4676-A58D-10573DA82CFE}.Release|ARM.Build.0 = Release|Any CPU + {7C67BFB9-1B3B-4676-A58D-10573DA82CFE}.Release|x64.ActiveCfg = Release|Any CPU + {7C67BFB9-1B3B-4676-A58D-10573DA82CFE}.Release|x64.Build.0 = Release|Any CPU + {7C67BFB9-1B3B-4676-A58D-10573DA82CFE}.Release|x86.ActiveCfg = Release|Any CPU + {7C67BFB9-1B3B-4676-A58D-10573DA82CFE}.Release|x86.Build.0 = Release|Any CPU + {5CAADB66-1FC2-4492-B766-36354687120D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5CAADB66-1FC2-4492-B766-36354687120D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5CAADB66-1FC2-4492-B766-36354687120D}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {5CAADB66-1FC2-4492-B766-36354687120D}.Debug|ARM.ActiveCfg = Debug|ARM + {5CAADB66-1FC2-4492-B766-36354687120D}.Debug|ARM.Build.0 = Debug|ARM + {5CAADB66-1FC2-4492-B766-36354687120D}.Debug|ARM.Deploy.0 = Debug|ARM + {5CAADB66-1FC2-4492-B766-36354687120D}.Debug|x64.ActiveCfg = Debug|x64 + {5CAADB66-1FC2-4492-B766-36354687120D}.Debug|x64.Build.0 = Debug|x64 + {5CAADB66-1FC2-4492-B766-36354687120D}.Debug|x64.Deploy.0 = Debug|x64 + {5CAADB66-1FC2-4492-B766-36354687120D}.Debug|x86.ActiveCfg = Debug|x86 + {5CAADB66-1FC2-4492-B766-36354687120D}.Debug|x86.Build.0 = Debug|x86 + {5CAADB66-1FC2-4492-B766-36354687120D}.Debug|x86.Deploy.0 = Debug|x86 + {5CAADB66-1FC2-4492-B766-36354687120D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5CAADB66-1FC2-4492-B766-36354687120D}.Release|Any CPU.Build.0 = Release|Any CPU + {5CAADB66-1FC2-4492-B766-36354687120D}.Release|Any CPU.Deploy.0 = Release|Any CPU + {5CAADB66-1FC2-4492-B766-36354687120D}.Release|ARM.ActiveCfg = Release|ARM + {5CAADB66-1FC2-4492-B766-36354687120D}.Release|ARM.Build.0 = Release|ARM + {5CAADB66-1FC2-4492-B766-36354687120D}.Release|ARM.Deploy.0 = Release|ARM + {5CAADB66-1FC2-4492-B766-36354687120D}.Release|x64.ActiveCfg = Release|x64 + {5CAADB66-1FC2-4492-B766-36354687120D}.Release|x64.Build.0 = Release|x64 + {5CAADB66-1FC2-4492-B766-36354687120D}.Release|x64.Deploy.0 = Release|x64 + {5CAADB66-1FC2-4492-B766-36354687120D}.Release|x86.ActiveCfg = Release|x86 + {5CAADB66-1FC2-4492-B766-36354687120D}.Release|x86.Build.0 = Release|x86 + {5CAADB66-1FC2-4492-B766-36354687120D}.Release|x86.Deploy.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/BuildFeed/Areas/admin/Controllers/usersController.cs b/BuildFeed/Areas/admin/Controllers/usersController.cs index 0df83a7..66a94aa 100644 --- a/BuildFeed/Areas/admin/Controllers/usersController.cs +++ b/BuildFeed/Areas/admin/Controllers/usersController.cs @@ -1,8 +1,8 @@ -using System; +using MongoAuth; +using System; using System.Linq; using System.Web.Mvc; using System.Web.Security; -using RedisAuth; namespace BuildFeed.Areas.admin.Controllers { @@ -41,28 +41,28 @@ public ActionResult demote(string id) public ActionResult approve(Guid id) { - RedisMembershipProvider provider = (Membership.Provider as RedisMembershipProvider); + MongoMembershipProvider provider = (Membership.Provider as MongoMembershipProvider); provider?.ChangeApproval(id, true); return RedirectToAction("Index"); } public ActionResult unapprove(Guid id) { - RedisMembershipProvider provider = (Membership.Provider as RedisMembershipProvider); + MongoMembershipProvider provider = (Membership.Provider as MongoMembershipProvider); provider?.ChangeApproval(id, false); return RedirectToAction("Index"); } public ActionResult @lock(Guid id) { - RedisMembershipProvider provider = (Membership.Provider as RedisMembershipProvider); + MongoMembershipProvider provider = (Membership.Provider as MongoMembershipProvider); provider?.ChangeLockStatus(id, true); return RedirectToAction("Index"); } public ActionResult unlock(Guid id) { - RedisMembershipProvider provider = (Membership.Provider as RedisMembershipProvider); + MongoMembershipProvider provider = (Membership.Provider as MongoMembershipProvider); provider?.ChangeLockStatus(id, false); return RedirectToAction("Index"); } diff --git a/BuildFeed/BuildFeed.csproj b/BuildFeed/BuildFeed.csproj index 9bbef10..3e1e269 100644 --- a/BuildFeed/BuildFeed.csproj +++ b/BuildFeed/BuildFeed.csproj @@ -393,12 +393,6 @@ - - - {c6a16cf0-41da-4b90-918a-cb84a8c3f1e2} - RedisAuth - - PublicResXFileCodeGenerator @@ -431,6 +425,12 @@ Support.Designer.cs + + + {7c67bfb9-1b3b-4676-a58d-10573da82cfe} + MongoAuth + + 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) diff --git a/BuildFeed/Controllers/frontController.cs b/BuildFeed/Controllers/frontController.cs index e087cae..c11ed9c 100644 --- a/BuildFeed/Controllers/frontController.cs +++ b/BuildFeed/Controllers/frontController.cs @@ -115,7 +115,7 @@ public ActionResult twitterCard(long id) gr.SmoothingMode = SmoothingMode.HighQuality; gr.PixelOffsetMode = PixelOffsetMode.HighQuality; - gr.FillRectangle(new SolidBrush(Color.FromArgb(0x30, 0x30, 0x30)), 0, 0, 560, 300); + gr.FillRectangle(new SolidBrush(Color.FromArgb(0x27, 0x2b, 0x30)), 0, 0, 560, 300); gp.AddString("BUILDFEED", new FontFamily("Segoe UI"), (int) FontStyle.Bold, 16, new Point(20, 20), StringFormat.GenericTypographic); gp.AddString($"Windows NT {b.MajorVersion}.{b.MinorVersion} build", new FontFamily("Segoe UI"), 0, 24, new Point(20, 40), StringFormat.GenericTypographic); gp.AddString(b.Number.ToString(), new FontFamily("Segoe UI Light"), 0, 180, new Point(12, 20), StringFormat.GenericTypographic); diff --git a/BuildFeed/Web.config b/BuildFeed/Web.config index e066f80..5343bb4 100644 --- a/BuildFeed/Web.config +++ b/BuildFeed/Web.config @@ -13,6 +13,7 @@ + @@ -28,13 +29,13 @@ - + - + diff --git a/BuildFeedApp-Westminster/BuildFeedApp-Westminster.jsproj b/BuildFeedApp-Westminster/BuildFeedApp-Westminster.jsproj new file mode 100644 index 0000000..eb7f0c9 --- /dev/null +++ b/BuildFeedApp-Westminster/BuildFeedApp-Westminster.jsproj @@ -0,0 +1,97 @@ + + + + + Debug + AnyCPU + + + Debug + ARM + + + Debug + x64 + + + Debug + x86 + + + Release + AnyCPU + + + Release + ARM + true + + + Release + x64 + true + + + Release + x86 + true + + + + 5caadb66-1fc2-4492-b766-36354687120d + + + + 14.0 + + + + + true + UAP + 10.0.10240.0 + 10.0.10240.0 + $(VersionNumberMajor).$(VersionNumberMinor) + en-US + BuildFeedApp-Westminster_TemporaryKey.pfx + + + + Designer + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/BuildFeedApp-Westminster/images/splashscreen.scale-125.png b/BuildFeedApp-Westminster/images/splashscreen.scale-125.png new file mode 100644 index 0000000..d8bbe72 Binary files /dev/null and b/BuildFeedApp-Westminster/images/splashscreen.scale-125.png differ diff --git a/BuildFeedApp-Westminster/images/splashscreen.scale-150.png b/BuildFeedApp-Westminster/images/splashscreen.scale-150.png new file mode 100644 index 0000000..f161a7a Binary files /dev/null and b/BuildFeedApp-Westminster/images/splashscreen.scale-150.png differ diff --git a/BuildFeedApp-Westminster/images/splashscreen.scale-200.png b/BuildFeedApp-Westminster/images/splashscreen.scale-200.png new file mode 100644 index 0000000..31f9ddc Binary files /dev/null and b/BuildFeedApp-Westminster/images/splashscreen.scale-200.png differ diff --git a/BuildFeedApp-Westminster/images/splashscreen.scale-400.png b/BuildFeedApp-Westminster/images/splashscreen.scale-400.png new file mode 100644 index 0000000..db0acfe Binary files /dev/null and b/BuildFeedApp-Westminster/images/splashscreen.scale-400.png differ diff --git a/BuildFeedApp-Westminster/images/tiles/square/150px.scale-100.png b/BuildFeedApp-Westminster/images/tiles/square/150px.scale-100.png new file mode 100644 index 0000000..afecf9a Binary files /dev/null and b/BuildFeedApp-Westminster/images/tiles/square/150px.scale-100.png differ diff --git a/BuildFeedApp-Westminster/images/tiles/square/150px.scale-125.png b/BuildFeedApp-Westminster/images/tiles/square/150px.scale-125.png new file mode 100644 index 0000000..13fe080 Binary files /dev/null and b/BuildFeedApp-Westminster/images/tiles/square/150px.scale-125.png differ diff --git a/BuildFeedApp-Westminster/images/tiles/square/150px.scale-150.png b/BuildFeedApp-Westminster/images/tiles/square/150px.scale-150.png new file mode 100644 index 0000000..da5558a Binary files /dev/null and b/BuildFeedApp-Westminster/images/tiles/square/150px.scale-150.png differ diff --git a/BuildFeedApp-Westminster/images/tiles/square/150px.scale-200.png b/BuildFeedApp-Westminster/images/tiles/square/150px.scale-200.png new file mode 100644 index 0000000..7d85792 Binary files /dev/null and b/BuildFeedApp-Westminster/images/tiles/square/150px.scale-200.png differ diff --git a/BuildFeedApp-Westminster/images/tiles/square/150px.scale-400.png b/BuildFeedApp-Westminster/images/tiles/square/150px.scale-400.png new file mode 100644 index 0000000..505002c Binary files /dev/null and b/BuildFeedApp-Westminster/images/tiles/square/150px.scale-400.png differ diff --git a/BuildFeedApp-Westminster/images/tiles/square/71px.scale-100.png b/BuildFeedApp-Westminster/images/tiles/square/71px.scale-100.png new file mode 100644 index 0000000..550ae94 Binary files /dev/null and b/BuildFeedApp-Westminster/images/tiles/square/71px.scale-100.png differ diff --git a/BuildFeedApp-Westminster/images/tiles/square/71px.scale-125.png b/BuildFeedApp-Westminster/images/tiles/square/71px.scale-125.png new file mode 100644 index 0000000..aedf876 Binary files /dev/null and b/BuildFeedApp-Westminster/images/tiles/square/71px.scale-125.png differ diff --git a/BuildFeedApp-Westminster/images/tiles/square/71px.scale-150.png b/BuildFeedApp-Westminster/images/tiles/square/71px.scale-150.png new file mode 100644 index 0000000..74b6cb1 Binary files /dev/null and b/BuildFeedApp-Westminster/images/tiles/square/71px.scale-150.png differ diff --git a/BuildFeedApp-Westminster/images/tiles/square/71px.scale-200.png b/BuildFeedApp-Westminster/images/tiles/square/71px.scale-200.png new file mode 100644 index 0000000..71cf680 Binary files /dev/null and b/BuildFeedApp-Westminster/images/tiles/square/71px.scale-200.png differ diff --git a/BuildFeedApp-Westminster/images/tiles/square/71px.scale-400.png b/BuildFeedApp-Westminster/images/tiles/square/71px.scale-400.png new file mode 100644 index 0000000..c7139d6 Binary files /dev/null and b/BuildFeedApp-Westminster/images/tiles/square/71px.scale-400.png differ diff --git a/BuildFeedApp-Westminster/images/tiles/wide/620px.scale-100.png b/BuildFeedApp-Westminster/images/tiles/wide/620px.scale-100.png new file mode 100644 index 0000000..feeb5e9 Binary files /dev/null and b/BuildFeedApp-Westminster/images/tiles/wide/620px.scale-100.png differ diff --git a/BuildFeedApp-Westminster/images/tiles/wide/620px.scale-125.png b/BuildFeedApp-Westminster/images/tiles/wide/620px.scale-125.png new file mode 100644 index 0000000..d8bbe72 Binary files /dev/null and b/BuildFeedApp-Westminster/images/tiles/wide/620px.scale-125.png differ diff --git a/BuildFeedApp-Westminster/images/tiles/wide/620px.scale-150.png b/BuildFeedApp-Westminster/images/tiles/wide/620px.scale-150.png new file mode 100644 index 0000000..f161a7a Binary files /dev/null and b/BuildFeedApp-Westminster/images/tiles/wide/620px.scale-150.png differ diff --git a/BuildFeedApp-Westminster/images/tiles/wide/620px.scale-200.png b/BuildFeedApp-Westminster/images/tiles/wide/620px.scale-200.png new file mode 100644 index 0000000..31f9ddc Binary files /dev/null and b/BuildFeedApp-Westminster/images/tiles/wide/620px.scale-200.png differ diff --git a/BuildFeedApp-Westminster/images/tiles/wide/620px.scale-400.png b/BuildFeedApp-Westminster/images/tiles/wide/620px.scale-400.png new file mode 100644 index 0000000..db0acfe Binary files /dev/null and b/BuildFeedApp-Westminster/images/tiles/wide/620px.scale-400.png differ diff --git a/BuildFeedApp-Westminster/package.appxmanifest b/BuildFeedApp-Westminster/package.appxmanifest new file mode 100644 index 0000000..ca7fad8 --- /dev/null +++ b/BuildFeedApp-Westminster/package.appxmanifest @@ -0,0 +1,31 @@ + + + + + + BuildFeedAppWestminster + Thomas Hounsell + images\storelogo.png + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MongoAuth/DatabaseConfig.cs b/MongoAuth/DatabaseConfig.cs new file mode 100644 index 0000000..888337d --- /dev/null +++ b/MongoAuth/DatabaseConfig.cs @@ -0,0 +1,26 @@ +using System.Configuration; + +namespace MongoAuth +{ + internal static class DatabaseConfig + { + public static string Host { get; private set; } + public static int Port { get; private set; } + public static string Database { get; private set; } + + static DatabaseConfig() + { + Host = !string.IsNullOrEmpty(ConfigurationManager.AppSettings["data:MongoHost"]) ? ConfigurationManager.AppSettings["data:MongoHost"] : "localhost"; + + int _port; + bool success = int.TryParse(ConfigurationManager.AppSettings["data:MongoPort"], out _port); + if (!success) + { + _port = 27017; // mongo default port + } + Port = _port; + + Database = !string.IsNullOrEmpty(ConfigurationManager.AppSettings["data:MongoDB"]) ? ConfigurationManager.AppSettings["data:MongoDB"] : "MongoAuth"; + } + } +} \ No newline at end of file diff --git a/MongoAuth/MongoAuth.csproj b/MongoAuth/MongoAuth.csproj new file mode 100644 index 0000000..f5812b8 --- /dev/null +++ b/MongoAuth/MongoAuth.csproj @@ -0,0 +1,73 @@ + + + + + Debug + AnyCPU + {7C67BFB9-1B3B-4676-A58D-10573DA82CFE} + Library + Properties + MongoAuth + MongoAuth + v4.5.2 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\MongoDB.Bson.2.0.1\lib\net45\MongoDB.Bson.dll + True + + + ..\packages\MongoDB.Driver.2.0.1\lib\net45\MongoDB.Driver.dll + True + + + ..\packages\MongoDB.Driver.Core.2.0.1\lib\net45\MongoDB.Driver.Core.dll + True + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MongoAuth/MongoMembershipProvider.cs b/MongoAuth/MongoMembershipProvider.cs new file mode 100644 index 0000000..bee5552 --- /dev/null +++ b/MongoAuth/MongoMembershipProvider.cs @@ -0,0 +1,457 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; +using System.Web.Security; + +namespace MongoAuth +{ + public class MongoMembershipProvider : MembershipProvider + { + private const string _CollectionName = "members"; + + private bool _enablePasswordReset = true; + private int _maxInvalidPasswordAttempts = 5; + private int _minRequiredNonAlphanumericCharacters = 1; + private int _minRequriedPasswordLength = 12; + private int _passwordAttemptWindow = 60; + private bool _requiresUniqueEmail = true; + + private MongoClient _dbClient; + + public override string ApplicationName { get; 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); + + _dbClient = new MongoClient(new MongoClientSettings() + { + Server = new MongoServerAddress(DatabaseConfig.Host, DatabaseConfig.Port) + }); + } + + public override bool ChangePassword(string username, string oldPassword, string newPassword) + { + bool isAuthenticated = ValidateUser(username, oldPassword); + + if (isAuthenticated) + { + var collection = _dbClient.GetDatabase(DatabaseConfig.Database).GetCollection(_CollectionName); + + var mmTask = collection.Find(m => m.UserName.ToLower() == username.ToLower()).SingleOrDefaultAsync(); + mmTask.Wait(); + var mm = mmTask.Result; + + if (mm == null) + { + return false; + } + + byte[] salt = new byte[24]; + byte[] hash = calculateHash(newPassword, ref salt); + + mm.PassSalt = salt; + mm.PassHash = hash; + + var replaceTask = collection.ReplaceOneAsync(m => m.Id == mm.Id, mm); + replaceTask.Wait(); + + if (replaceTask.IsCompleted) + { + return true; + } + + return false; + } + + 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; + + var collection = _dbClient.GetDatabase(DatabaseConfig.Database).GetCollection(_CollectionName); + + var dupeUsers = collection.Find(m => m.UserName.ToLower() == username.ToLower()).CountAsync(); + var dupeEmails = collection.Find(m => m.EmailAddress.ToLower() == email.ToLower()).CountAsync(); + + dupeUsers.Wait(); + dupeEmails.Wait(); + + if (dupeUsers.Result > 0) + { + status = MembershipCreateStatus.DuplicateUserName; + } + else if (dupeEmails.Result > 0) + { + status = MembershipCreateStatus.DuplicateEmail; + } + else + { + byte[] salt = new byte[24]; + byte[] hash = calculateHash(password, ref salt); + + MongoMember mm = new MongoMember() + { + 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 + }; + + var insertTask = collection.InsertOneAsync(mm); + insertTask.Wait(); + + if (insertTask.Status == TaskStatus.RanToCompletion) + { + + status = MembershipCreateStatus.Success; + mu = new MembershipUser(this.Name, mm.UserName, mm.Id, mm.EmailAddress, "", "", mm.IsApproved, mm.IsLockedOut, mm.CreationDate, mm.LastLoginDate, mm.LastActivityDate, DateTime.MinValue, mm.LastLockoutDate); + } + else + { + status = MembershipCreateStatus.ProviderError; + } + } + + return mu; + } + + public override bool DeleteUser(string username, bool deleteAllRelatedData) + { + var collection = _dbClient.GetDatabase(DatabaseConfig.Database).GetCollection(_CollectionName); + + var deleteTask = collection.DeleteOneAsync(m => m.UserName.ToLower() == m.UserName.ToLower()); + deleteTask.Wait(); + + if (deleteTask.Result.IsAcknowledged && deleteTask.Result.DeletedCount == 1) + { + return true; + } + + 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(); + + var collection = _dbClient.GetDatabase(DatabaseConfig.Database).GetCollection(_CollectionName); + + var users = collection.Find(new BsonDocument()); + + var totalRecordsTask = users.CountAsync(); + totalRecordsTask.Wait(); + totalRecords = Convert.ToInt32(totalRecordsTask.Result); + + users = users.Skip(pageIndex * pageSize).Limit(pageSize); + + var pageItemsTask = users.ToListAsync(); + pageItemsTask.Wait(); + + foreach (var mm in pageItemsTask.Result) + { + muc.Add(new MembershipUser(this.Name, mm.UserName, mm.Id, mm.EmailAddress, "", "", mm.IsApproved, mm.IsLockedOut, mm.CreationDate, mm.LastLoginDate, mm.LastActivityDate, DateTime.MinValue, mm.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) + { + var collection = _dbClient.GetDatabase(DatabaseConfig.Database).GetCollection(_CollectionName); + + var mmTask = collection.Find(f => f.UserName.ToLower() == username.ToLower()).FirstOrDefaultAsync(); + mmTask.Wait(); + + var mm = mmTask.Result; + + return mm == null ? null : new MembershipUser(this.Name, mm.UserName, mm.Id, mm.EmailAddress, "", "", mm.IsApproved, mm.IsLockedOut, mm.CreationDate, mm.LastLoginDate, mm.LastActivityDate, DateTime.MinValue, mm.LastLockoutDate); + } + + public override MembershipUser GetUser(object providerUserKey, bool userIsOnline) + { + var collection = _dbClient.GetDatabase(DatabaseConfig.Database).GetCollection(_CollectionName); + + var mmTask = collection.Find(f => f.Id == (Guid)providerUserKey).FirstOrDefaultAsync(); + mmTask.Wait(); + + var mm = mmTask.Result; + + return mm == null ? null : new MembershipUser(this.Name, mm.UserName, mm.Id, mm.EmailAddress, "", "", mm.IsApproved, mm.IsLockedOut, mm.CreationDate, mm.LastLoginDate, mm.LastActivityDate, DateTime.MinValue, mm.LastLockoutDate); + } + + public override string GetUserNameByEmail(string email) + { + var collection = _dbClient.GetDatabase(DatabaseConfig.Database).GetCollection(_CollectionName); + + var mmTask = collection.Find(f => f.EmailAddress.ToLower() == email.ToLower()).FirstOrDefaultAsync(); + mmTask.Wait(); + + var mm = mmTask.Result; + + return mm.UserName; + } + + public override string ResetPassword(string username, string answer) + { + throw new NotImplementedException(); + } + + public void ChangeApproval(Guid Id, bool newStatus) + { + var collection = _dbClient.GetDatabase(DatabaseConfig.Database).GetCollection(_CollectionName); + var task = collection.UpdateOneAsync( + Builders.Filter.Eq(u => u.Id, Id), + Builders.Update.Set(u => u.IsApproved, newStatus) + ); + task.Wait(); + } + + public void ChangeLockStatus(Guid Id, bool newStatus) + { + var collection = _dbClient.GetDatabase(DatabaseConfig.Database).GetCollection(_CollectionName); + + + var updateDefinition = new List>(); + updateDefinition.Add(Builders.Update.Set(u => u.IsLockedOut, newStatus)); + + if (newStatus) + { + updateDefinition.Add(Builders.Update.Set(u => u.LastLockoutDate, DateTime.Now)); + } + else + { + updateDefinition.Add(Builders.Update.Set(u => u.LockoutWindowAttempts, 0)); + updateDefinition.Add(Builders.Update.Set(u => u.LastLockoutDate, DateTime.MinValue)); + } + + var task = collection.UpdateOneAsync( + Builders.Filter.Eq(u => u.Id, Id), + Builders.Update.Combine(updateDefinition) + ); + task.Wait(); + } + + public override bool UnlockUser(string userName) + { + var collection = _dbClient.GetDatabase(DatabaseConfig.Database).GetCollection(_CollectionName); + var updTask = collection.UpdateOneAsync(Builders.Filter.Eq(m => m.UserName.ToLower(), userName.ToLower()), Builders.Update.Set(m => m.IsLockedOut, false)); + + updTask.Wait(); + + return updTask.Result.IsAcknowledged && updTask.Result.ModifiedCount == 1; + } + + public override void UpdateUser(MembershipUser user) + { + throw new NotImplementedException(); + } + + public override bool ValidateUser(string username, string password) + { + var collection = _dbClient.GetDatabase(DatabaseConfig.Database).GetCollection(_CollectionName); + + var mmTask = collection.Find(f => f.UserName.ToLower() == username.ToLower()).FirstOrDefaultAsync(); + mmTask.Wait(); + + var mm = mmTask.Result; + + + if (mm == null || !(mm.IsApproved && !mm.IsLockedOut)) + { + return false; + } + + byte[] salt = mm.PassSalt; + byte[] hash = calculateHash(password, ref salt); + + bool isFail = false; + + for (int i = 0; i > hash.Length; i++) + { + isFail |= (hash[i] != mm.PassHash[i]); + } + + if (isFail) + { + // increment failed counter and lock out if required. + } + else + { + // reset failed counter + } + + return !isFail; + } + + 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; + } + + 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; + } + } + + public class MongoMember + { + [BsonId] + public Guid Id { get; set; } + + public string UserName { get; set; } + public byte[] PassHash { get; set; } + public byte[] PassSalt { get; set; } + 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; } + } + +} diff --git a/MongoAuth/MongoRoleProvider.cs b/MongoAuth/MongoRoleProvider.cs new file mode 100644 index 0000000..c412434 --- /dev/null +++ b/MongoAuth/MongoRoleProvider.cs @@ -0,0 +1,247 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Web.Security; +using System.Collections.Specialized; + +namespace MongoAuth +{ + public class MongoRoleProvider : RoleProvider + { + private const string _RoleCollectionName = "roles"; + private const string _MemberCollectionName = "members"; + private MongoClient _dbClient; + + public override string ApplicationName + { + get { return ""; } + set { } + } + + public override void Initialize(string name, NameValueCollection config) + { + base.Initialize(name, config); + + _dbClient = new MongoClient(new MongoClientSettings() + { + Server = new MongoServerAddress(DatabaseConfig.Host, DatabaseConfig.Port) + }); + } + + public override void AddUsersToRoles(string[] usernames, string[] roleNames) + { + var roleCollection = _dbClient.GetDatabase(DatabaseConfig.Database).GetCollection(_RoleCollectionName); + var memberCollection = _dbClient.GetDatabase(DatabaseConfig.Database).GetCollection(_MemberCollectionName); + + var roleTask = roleCollection.Find(r => roleNames.Contains(r.RoleName)).ToListAsync(); + roleTask.Wait(); + List roles = roleTask.Result; + + var userTask = memberCollection.Find(u => usernames.Contains(u.UserName)).ToListAsync(); + userTask.Wait(); + List users = userTask.Result; + + for (int i = 0; i < roles.Count; i++) + { + List newUsers = new List(); + + if (roles[i].Users != null) + { + newUsers.AddRange(roles[i].Users); + } + + var usersToAdd = from u in users + where !newUsers.Any(v => v == u.Id) + select u.Id; + + newUsers.AddRange(usersToAdd); + + roles[i].Users = newUsers.ToArray(); + + var update = roleCollection.ReplaceOneAsync(Builders.Filter.Eq(r => r.Id, roles[i].Id), roles[i]); + update.Wait(); + } + } + + public override void CreateRole(string roleName) + { + var roleCollection = _dbClient.GetDatabase(DatabaseConfig.Database).GetCollection(_RoleCollectionName); + + MongoRole r = new MongoRole() + { + Id = Guid.NewGuid(), + RoleName = roleName + }; + + var task = roleCollection.InsertOneAsync(r); + task.Wait(); + } + + public override bool DeleteRole(string roleName, bool throwOnPopulatedRole) + { + var roleCollection = _dbClient.GetDatabase(DatabaseConfig.Database).GetCollection(_RoleCollectionName); + + var role = roleCollection.Find(r => r.RoleName == roleName).SingleOrDefaultAsync(); + role.Wait(); + + if (role.Result != null && role.Result.Users.Length > 0 && throwOnPopulatedRole) + { + throw new Exception("This role still has users"); + } + + roleCollection.DeleteOneAsync(r => r.RoleName == roleName); + return true; + } + + public override string[] FindUsersInRole(string roleName, string usernameToMatch) + { + var roleCollection = _dbClient.GetDatabase(DatabaseConfig.Database).GetCollection(_RoleCollectionName); + var memberCollection = _dbClient.GetDatabase(DatabaseConfig.Database).GetCollection(_MemberCollectionName); + + var role = roleCollection.Find(r => r.RoleName == roleName).SingleOrDefaultAsync(); + role.Wait(); + + if (role == null) + { + throw new Exception("Role does not exist"); + } + + var users = memberCollection.Find(u => role.Result.Users.Contains(u.Id) && u.UserName.ToLower().Contains(usernameToMatch.ToLower())).ToListAsync(); + users.Wait(); + + return users.Result + .Select(r => r.UserName) + .ToArray(); + } + + public override string[] GetAllRoles() + { + var roleCollection = _dbClient.GetDatabase(DatabaseConfig.Database).GetCollection(_RoleCollectionName); + var roles = roleCollection.Find(new BsonDocument()).ToListAsync(); + roles.Wait(); + return roles.Result + .Select(r => r.RoleName) + .ToArray(); + } + + public override string[] GetRolesForUser(string username) + { + var roleCollection = _dbClient.GetDatabase(DatabaseConfig.Database).GetCollection(_RoleCollectionName); + var memberCollection = _dbClient.GetDatabase(DatabaseConfig.Database).GetCollection(_MemberCollectionName); + + var user = memberCollection.Find(u => u.UserName.ToLower() == username.ToLower()).SingleOrDefaultAsync(); + user.Wait(); + + if (user == null) + { + throw new Exception("User does not exist"); + } + + var role = roleCollection.Find(new BsonDocument()).ToListAsync(); + role.Wait(); + + return (from r in role.Result + where r.Users != null + where r.Users.Any(u => u == user.Result.Id) + select r.RoleName).ToArray(); + } + + public override string[] GetUsersInRole(string roleName) + { + var roleCollection = _dbClient.GetDatabase(DatabaseConfig.Database).GetCollection(_RoleCollectionName); + var memberCollection = _dbClient.GetDatabase(DatabaseConfig.Database).GetCollection(_MemberCollectionName); + + var role = roleCollection.Find(r => r.RoleName == roleName).SingleOrDefaultAsync(); + role.Wait(); + + if (role == null) + { + throw new Exception("Role does not exist"); + } + + var users = memberCollection.Find(u => role.Result.Users.Contains(u.Id)).ToListAsync(); + users.Wait(); + + return users.Result + .Select(u => u.UserName) + .ToArray(); + } + + public override bool IsUserInRole(string username, string roleName) + { + var roleCollection = _dbClient.GetDatabase(DatabaseConfig.Database).GetCollection(_RoleCollectionName); + var memberCollection = _dbClient.GetDatabase(DatabaseConfig.Database).GetCollection(_MemberCollectionName); + + var user = memberCollection.Find(u => u.UserName.ToLower() == username.ToLower()).SingleOrDefaultAsync(); + user.Wait(); + + if (user.Result == null) + { + throw new Exception("User does not exist"); + } + + var role = roleCollection.Find(r => r.RoleName == roleName).SingleOrDefaultAsync(); + + if (role.Result == null) + { + throw new Exception("Role does not exist"); + } + + if (role.Result.Users == null) + { + return false; + } + + return role.Result.Users.Any(u => u == user.Result.Id); + } + + public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames) + { + var roleCollection = _dbClient.GetDatabase(DatabaseConfig.Database).GetCollection(_RoleCollectionName); + var memberCollection = _dbClient.GetDatabase(DatabaseConfig.Database).GetCollection(_MemberCollectionName); + + var roleTask = roleCollection.Find(r => roleNames.Any(n => n == r.RoleName)).ToListAsync(); + roleTask.Wait(); + List roles = roleTask.Result; + + var userTask = memberCollection.Find(u => usernames.Any(n => n == u.UserName)).ToListAsync(); + userTask.Wait(); + List users = userTask.Result; + + 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(); + + var update = roleCollection.ReplaceOneAsync(Builders.Filter.Eq(r => r.Id, roles[i].Id), roles[i]); + update.Wait(); + } + } + + public override bool RoleExists(string roleName) + { + var roleCollection = _dbClient.GetDatabase(DatabaseConfig.Database).GetCollection(_RoleCollectionName); + + var role = roleCollection.Find(r => r.RoleName == roleName).SingleOrDefaultAsync(); + role.Wait(); + + return role.Result != null; + } + } + + [DataObject] + public class MongoRole + { + [BsonId] + public Guid Id { get; set; } + + public string RoleName { get; set; } + + public Guid[] Users { get; set; } + } +} \ No newline at end of file diff --git a/MongoAuth/Properties/AssemblyInfo.cs b/MongoAuth/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..0630ad8 --- /dev/null +++ b/MongoAuth/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MongoAuth")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MongoAuth")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("7c67bfb9-1b3b-4676-a58d-10573da82cfe")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/MongoAuth/packages.config b/MongoAuth/packages.config new file mode 100644 index 0000000..2f95291 --- /dev/null +++ b/MongoAuth/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file