diff --git a/src/Orchard.Specs/Bindings/ContentRights.cs b/src/Orchard.Specs/Bindings/ContentRights.cs index 75b5291b8..837659ad2 100644 --- a/src/Orchard.Specs/Bindings/ContentRights.cs +++ b/src/Orchard.Specs/Bindings/ContentRights.cs @@ -15,41 +15,6 @@ namespace Orchard.Specs.Bindings { [Binding] public class ContentRights : BindingBase { - [When(@"I have a role ""(.*)\"" with permissions ""(.*)\""")] - public void WhenIHaveARoleWithPermissions(string roleName, string permissions) { - var webApp = Binding(); - webApp.Host.Execute(() => { - using ( var environment = MvcApplication.CreateStandaloneEnvironment("Default") ) { - var roleService = environment.Resolve(); - - roleService.CreateRole(roleName); - - foreach ( var permissionName in permissions.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries) ) { - roleService.CreatePermissionForRole(roleName, permissionName); - } - } - }); - } - - [When(@"I have a user ""(.*)\"" with roles ""(.*)\""")] - public void GivenIHaveCreatedAUser(string username, string roles) { - - var webApp = Binding(); - webApp.Host.Execute(() => { - using ( var environment = MvcApplication.CreateStandaloneEnvironment("Default") ) { - var memberShipService = environment.Resolve(); - var roleService = environment.Resolve(); - var userRoleRepository = environment.Resolve>(); - var user = memberShipService.CreateUser(new CreateUserParams(username, "qwerty123!", username + "@foo.com", "", "", true)); - - foreach ( var roleName in roles.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries) ) { - var role = roleService.GetRoleByName(roleName); - userRoleRepository.Create(new UserRolesPartRecord { UserId = user.Id, Role = role }); - } - } - }); - } - [Then(@"""(.*)\"" should be able to ""(.*)\"" a ""(.*)\"" owned by ""(.*)\""")] public void UserShouldBeAbleToForOthers(string username, string action, string contentType, string otherName) { diff --git a/src/Orchard.Specs/Bindings/UsersPermissionsAndRoles.cs b/src/Orchard.Specs/Bindings/UsersPermissionsAndRoles.cs new file mode 100644 index 000000000..321b1b901 --- /dev/null +++ b/src/Orchard.Specs/Bindings/UsersPermissionsAndRoles.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Orchard.Data; +using Orchard.Roles.Models; +using Orchard.Roles.Services; +using Orchard.Security; +using Orchard.Specs.Hosting.Orchard.Web; +using TechTalk.SpecFlow; + +namespace Orchard.Specs.Bindings { + [Binding] + public class UsersPermissionsAndRoles : BindingBase { + + + [When(@"I have a role ""(.*)\"" with permissions ""(.*)\""")] + public void WhenIHaveARoleWithPermissions(string roleName, string permissions) { + var webApp = Binding(); + webApp.Host.Execute(() => { + using (var environment = MvcApplication.CreateStandaloneEnvironment("Default")) { + var roleService = environment.Resolve(); + + roleService.CreateRole(roleName); + + foreach (var permissionName in permissions.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)) { + roleService.CreatePermissionForRole(roleName, permissionName); + } + } + }); + } + + + [When(@"I have a user ""(.*)\"" with roles ""(.*)\""")] + public void GivenIHaveCreatedAUser(string username, string roles) { + + var webApp = Binding(); + webApp.Host.Execute(() => { + using (var environment = MvcApplication.CreateStandaloneEnvironment("Default")) { + var memberShipService = environment.Resolve(); + var roleService = environment.Resolve(); + var userRoleRepository = environment.Resolve>(); + var user = memberShipService.CreateUser(new CreateUserParams(username, "qwerty123!", username + "@foo.com", "", "", true)); + + foreach (var roleName in roles.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)) { + var role = roleService.GetRoleByName(roleName); + userRoleRepository.Create(new UserRolesPartRecord { UserId = user.Id, Role = role }); + } + } + }); + } + + [Given(@"I have a user ""(.*)"" with permissions ""(.*)""")] + public void GivenIHaveAUserWithPermissions(string username, string permissions) { + var roleName = Guid.NewGuid().ToString("n"); + WhenIHaveARoleWithPermissions(roleName, permissions); + GivenIHaveCreatedAUser(username, roleName); + } + + [When(@"I sign in as ""(.*)""")] + public void WhenISignInAs(string username) { + var webApp = Binding(); + var logonForm = TableData( + new { name = "userNameOrEmail", value = username }, + new { name = "password", value = "qwerty123!" }); + + webApp.WhenIGoTo("/users/account/logoff"); + webApp.WhenIGoTo("/users/account/logon"); + webApp.WhenIFillIn(logonForm); + webApp.WhenIHit("Sign In"); + webApp.WhenIAmRedirected(); + } + } +} diff --git a/src/Orchard.Specs/Bindings/WebAppHosting.cs b/src/Orchard.Specs/Bindings/WebAppHosting.cs index 437b88c30..e63150b55 100644 --- a/src/Orchard.Specs/Bindings/WebAppHosting.cs +++ b/src/Orchard.Specs/Bindings/WebAppHosting.cs @@ -294,20 +294,34 @@ namespace Orchard.Specs.Bindings { Assert.That(Details.ResponseHeaders["Content-Type"], Is.StringMatching(contentType)); } - [Then(@"I should see ""(.*)""")] + [Then(@"I should see ""([^""]*)""")] public void ThenIShouldSee(string text) { Assert.That(Details.ResponseText, Is.StringMatching(text)); } - [Then(@"I should not see ""(.*)""")] + [Then(@"I should not see ""([^""]*)""")] public void ThenIShouldNotSee(string text) { Assert.That(Details.ResponseText, Is.Not.StringContaining(text)); } - [Then(@"the title contains ""(.*)""")] + [Then(@"the title contains ""([^""]*)""")] public void ThenTheTitleContainsText(string text) { ScenarioContext.Current.Pending(); } + + + [Then(@"I should see ""([^""]*)"" when I go to ""([^""]*)""")] + public void ThenIShouldSeeWhenIGoTo(string text, string urlPath) { + WhenIGoTo(urlPath); + ThenIShouldSee(text); + } + + [Then(@"I should be denied access when I go to ""([^""]*)""")] + public void ThenIShouldBeDeniedAccessWhenIGoTo(string urlPath) { + WhenIGoTo(urlPath); + WhenIAmRedirected(); + ThenIShouldSee("Access Denied"); + } } public class Form { diff --git a/src/Orchard.Specs/Hosting/RequestExtensions.cs b/src/Orchard.Specs/Hosting/RequestExtensions.cs index 0da7f701d..5b0b584a9 100644 --- a/src/Orchard.Specs/Hosting/RequestExtensions.cs +++ b/src/Orchard.Specs/Hosting/RequestExtensions.cs @@ -23,9 +23,15 @@ namespace Orchard.Specs.Hosting { var details = new RequestDetails { HostName = webHost.HostName, UrlPath = urlPath, - Page = (isHomepage ? "" : physicalPath.Combine(urlPath.TrimStart('/', '\\')).GetRelativePath(physicalPath).ToString()) }; + int queryIndex = urlPath.IndexOf('?'); + if (queryIndex >= 0) { + details.UrlPath = urlPath.Substring(0, queryIndex); + details.Query = HttpUtility.UrlDecode(urlPath.Substring(queryIndex + 1)); + } + details.Page = (isHomepage ? "" : physicalPath.Combine(details.UrlPath.TrimStart('/', '\\')).GetRelativePath(physicalPath).ToString()); + if (!string.IsNullOrEmpty(webHost.Cookies)) { details.RequestHeaders.Add("Cookie", webHost.Cookies); } @@ -123,7 +129,7 @@ namespace Orchard.Specs.Hosting { if (_details.RequestHeaders.TryGetValue("Cookie", out value)) return value; } - else if (index==HeaderHost) { + else if (index == HeaderHost) { return _details.HostName; } return base.GetKnownRequestHeader(index); diff --git a/src/Orchard.Specs/Orchard.Specs.csproj b/src/Orchard.Specs/Orchard.Specs.csproj index 2c1d3c53d..495651fba 100644 --- a/src/Orchard.Specs/Orchard.Specs.csproj +++ b/src/Orchard.Specs/Orchard.Specs.csproj @@ -132,6 +132,7 @@ + True True @@ -180,6 +181,11 @@ True Pages.feature + + True + True + PermissionModel.feature + True True @@ -249,6 +255,10 @@ SpecFlowSingleFileGenerator ContentTypes.feature.cs + + SpecFlowSingleFileGenerator + PermissionModel.feature.cs + Designer Always diff --git a/src/Orchard.Specs/PermissionModel.feature b/src/Orchard.Specs/PermissionModel.feature new file mode 100644 index 000000000..e30f66b9f --- /dev/null +++ b/src/Orchard.Specs/PermissionModel.feature @@ -0,0 +1,27 @@ +Feature: Addition + In order to prevent security model regressions + As a user with specific permissions + I should to be granted or denied access to various actions + +@security +Scenario: Login can be automated + Given I have installed Orchard + And I have a user "bob" with permissions "AccessFrontEnd" + When I go to "users/account/logoff" + And I go to "users/account/logon" + And I fill in + | name | value | + | userNameOrEmail | bob | + | password | qwerty123! | + And I hit "Sign In" + And I am redirected + Then I should see "Welcome, bob!" + +@security +Scenario: Anonymous user can see the home page but not the dashboard + Given I have installed Orchard + And I have a user "bob" with permissions "AccessFrontEnd" + When I sign in as "bob" + Then I should see "this is the homepage of your new site" when I go to "/" + And I should be denied access when I go to "admin" + diff --git a/src/Orchard.Specs/PermissionModel.feature.cs b/src/Orchard.Specs/PermissionModel.feature.cs new file mode 100644 index 000000000..67557fede --- /dev/null +++ b/src/Orchard.Specs/PermissionModel.feature.cs @@ -0,0 +1,119 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by SpecFlow (http://www.specflow.org/). +// SpecFlow Version:1.4.0.0 +// Runtime Version:4.0.30319.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +#region Designer generated code +namespace Orchard.Specs +{ + using TechTalk.SpecFlow; + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.4.0.0")] + [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [NUnit.Framework.TestFixtureAttribute()] + [NUnit.Framework.DescriptionAttribute("Addition")] + public partial class AdditionFeature + { + + private static TechTalk.SpecFlow.ITestRunner testRunner; + +#line 1 "PermissionModel.feature" +#line hidden + + [NUnit.Framework.TestFixtureSetUpAttribute()] + public virtual void FeatureSetup() + { + testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner(); + TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "Addition", "In order to prevent security model regressions\nAs a user with specific permission" + + "s\nI should to be granted or denied access to various actions", GenerationTargetLanguage.CSharp, ((string[])(null))); + testRunner.OnFeatureStart(featureInfo); + } + + [NUnit.Framework.TestFixtureTearDownAttribute()] + public virtual void FeatureTearDown() + { + testRunner.OnFeatureEnd(); + testRunner = null; + } + + public virtual void ScenarioSetup(TechTalk.SpecFlow.ScenarioInfo scenarioInfo) + { + testRunner.OnScenarioStart(scenarioInfo); + } + + [NUnit.Framework.TearDownAttribute()] + public virtual void ScenarioTearDown() + { + testRunner.OnScenarioEnd(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("Login can be automated")] + [NUnit.Framework.CategoryAttribute("security")] + public virtual void LoginCanBeAutomated() + { + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Login can be automated", new string[] { + "security"}); +#line 7 +this.ScenarioSetup(scenarioInfo); +#line 8 + testRunner.Given("I have installed Orchard"); +#line 9 + testRunner.And("I have a user \"bob\" with permissions \"AccessFrontEnd\""); +#line 10 + testRunner.When("I go to \"users/account/logoff\""); +#line 11 + testRunner.And("I go to \"users/account/logon\""); +#line hidden + TechTalk.SpecFlow.Table table1 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table1.AddRow(new string[] { + "userNameOrEmail", + "bob"}); + table1.AddRow(new string[] { + "password", + "qwerty123!"}); +#line 12 + testRunner.And("I fill in", ((string)(null)), table1); +#line 16 + testRunner.And("I hit \"Sign In\""); +#line 17 + testRunner.And("I am redirected"); +#line 18 + testRunner.Then("I should see \"Welcome, bob!\""); +#line hidden + testRunner.CollectScenarioErrors(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("Anonymous user can see the home page but not the dashboard")] + [NUnit.Framework.CategoryAttribute("security")] + public virtual void AnonymousUserCanSeeTheHomePageButNotTheDashboard() + { + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Anonymous user can see the home page but not the dashboard", new string[] { + "security"}); +#line 21 +this.ScenarioSetup(scenarioInfo); +#line 22 + testRunner.Given("I have installed Orchard"); +#line 23 + testRunner.And("I have a user \"bob\" with permissions \"AccessFrontEnd\""); +#line 24 + testRunner.When("I sign in as \"bob\""); +#line 25 + testRunner.Then("I should see \"this is the homepage of your new site\" when I go to \"/\""); +#line 26 + testRunner.And("I should be denied access when I go to \"admin\""); +#line hidden + testRunner.CollectScenarioErrors(); + } + } +} +#endregion diff --git a/src/Orchard.sln b/src/Orchard.sln index e693992d6..10d7b6af4 100644 --- a/src/Orchard.sln +++ b/src/Orchard.sln @@ -102,6 +102,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Scripting.Dlr", "Or EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Scripting", "Orchard.Web\Modules\Orchard.Scripting\Orchard.Scripting.csproj", "{99002B65-86F7-415E-BF4A-381AA8AB9CCC}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Specs", "Specs", "{3E10BF6D-ADA5-417D-B36C-EBB0660B475E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution CodeCoverage|Any CPU = CodeCoverage|Any CPU @@ -587,12 +589,12 @@ Global {F112851D-B023-4746-B6B1-8D2E5AD8F7AA} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA} {6CB3EB30-F725-45C0-9742-42599BA8E8D2} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA} {2FC1D9C8-446D-4414-B252-5E9FBE61EB63} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA} - {7354DF37-934B-46CF-A13C-455D5F5F5413} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA} - {94E694A2-D140-468D-A277-C5FCE1D13E9B} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA} {5E5E7A21-C7B2-44D8-8593-2F9541AE041D} = {383DBA32-4A3E-48D1-AAC3-75377A694452} {4AB4B5B6-277E-4FF6-B69B-7AE9E16D2A56} = {383DBA32-4A3E-48D1-AAC3-75377A694452} {33B1BC8D-E292-4972-A363-22056B207156} = {383DBA32-4A3E-48D1-AAC3-75377A694452} {0DFA2E10-96C8-4E05-BC10-B710B97ECCDE} = {383DBA32-4A3E-48D1-AAC3-75377A694452} {CB70A642-8CEC-4DDE-8C9F-AD08900EC98D} = {74492CBC-7201-417E-BC29-28B4C25A58B0} + {7354DF37-934B-46CF-A13C-455D5F5F5413} = {3E10BF6D-ADA5-417D-B36C-EBB0660B475E} + {94E694A2-D140-468D-A277-C5FCE1D13E9B} = {3E10BF6D-ADA5-417D-B36C-EBB0660B475E} EndGlobalSection EndGlobal