From d23952af091cc51e9c9e238dfb318aca1f4df1e3 Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 16 Oct 2015 09:09:23 +0800 Subject: [PATCH 1/3] DNN-7706: hide delete/save button when edit global data types. --- DNN Platform/Dnn.DynamicContent/DataTypeManager.cs | 11 +++++++++++ .../ClientScripts/dataTypes.js | 2 +- .../Dnn.Modules.DynamicContentManager/Manager.html | 4 ++-- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/DNN Platform/Dnn.DynamicContent/DataTypeManager.cs b/DNN Platform/Dnn.DynamicContent/DataTypeManager.cs index 299b1ef1abf..cf52474a1a3 100644 --- a/DNN Platform/Dnn.DynamicContent/DataTypeManager.cs +++ b/DNN Platform/Dnn.DynamicContent/DataTypeManager.cs @@ -15,6 +15,7 @@ using DotNetNuke.Entities.Content; using DotNetNuke.Entities.Portals; using DotNetNuke.Entities.Users; +using DotNetNuke.Services.Exceptions; using DotNetNuke.Services.Localization; namespace Dnn.DynamicContent @@ -68,6 +69,11 @@ public void DeleteDataType(DataType dataType) Requires.PropertyNotNull(dataType, "DataTypeId"); Requires.PropertyNotNegative(dataType, "DataTypeId"); + if (dataType.IsSystem && !UserController.Instance.GetCurrentUserInfo().IsSuperUser) + { + throw new SecurityException("Global Data Types can only delete by host user"); + } + using (DataContext) { var fieldDefinitionRepository = DataContext.GetRepository(); @@ -164,6 +170,11 @@ public void UpdateDataType(DataType dataType, bool overrideWarning = false) Requires.PropertyNotNegative(dataType, "DataTypeId"); Requires.PropertyNotNullOrEmpty(dataType, "Name"); + if (dataType.IsSystem && !UserController.Instance.GetCurrentUserInfo().IsSuperUser) + { + throw new SecurityException("Global Data Types can only update by host user"); + } + dataType.LastModifiedByUserId = UserController.Instance.GetCurrentUserInfo().UserID; dataType.LastModifiedOnDate = DateUtilitiesManager.Instance.GetDatabaseTime(); diff --git a/DNN Platform/Modules/Dnn.Modules.DynamicContentManager/ClientScripts/dataTypes.js b/DNN Platform/Modules/Dnn.Modules.DynamicContentManager/ClientScripts/dataTypes.js index 357df9c2675..c6b67e2f531 100644 --- a/DNN Platform/Modules/Dnn.Modules.DynamicContentManager/ClientScripts/dataTypes.js +++ b/DNN Platform/Modules/Dnn.Modules.DynamicContentManager/ClientScripts/dataTypes.js @@ -209,7 +209,7 @@ dcc.dataTypeViewModel = function(parentViewModel, config){ self.init = function() { self.dataTypeId(-1); - self.canEdit(false); + self.canEdit(true); self.baseType(0); self.isSystem(false); diff --git a/DNN Platform/Modules/Dnn.Modules.DynamicContentManager/Manager.html b/DNN Platform/Modules/Dnn.Modules.DynamicContentManager/Manager.html index 3b56b01f33a..dca51307810 100644 --- a/DNN Platform/Modules/Dnn.Modules.DynamicContentManager/Manager.html +++ b/DNN Platform/Modules/Dnn.Modules.DynamicContentManager/Manager.html @@ -280,9 +280,9 @@
From d18fb5a437ebff458af4ecbef44448abb3fe96ad Mon Sep 17 00:00:00 2001 From: Francesco Rivola Date: Mon, 19 Oct 2015 17:11:10 +0200 Subject: [PATCH 2/3] DNN-7706 - Admin can delete any Data Type from the Data Types list view - added server side checks in add, update and delete data type to ensure that only Super Users can manage Global Data Types - added unit tests --- .../Dnn.DynamicContent/DataTypeManager.cs | 26 ++-- .../Dnn.DynamicContent.csproj | 1 + .../GlobalDataTypeSecurityException.cs | 20 +++ .../DataTypeManagerTests.cs | 124 ++++++++++++++---- Website/App_GlobalResources/Exceptions.resx | 3 + 5 files changed, 141 insertions(+), 33 deletions(-) create mode 100644 DNN Platform/Dnn.DynamicContent/Exceptions/GlobalDataTypeSecurityException.cs diff --git a/DNN Platform/Dnn.DynamicContent/DataTypeManager.cs b/DNN Platform/Dnn.DynamicContent/DataTypeManager.cs index cf52474a1a3..f2c27896761 100644 --- a/DNN Platform/Dnn.DynamicContent/DataTypeManager.cs +++ b/DNN Platform/Dnn.DynamicContent/DataTypeManager.cs @@ -4,19 +4,14 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Web.UI; using Dnn.DynamicContent.Common; using Dnn.DynamicContent.Exceptions; using Dnn.DynamicContent.Localization; using DotNetNuke.Collections; using DotNetNuke.Common; -using DotNetNuke.Common.Utilities; using DotNetNuke.Data; using DotNetNuke.Entities.Content; -using DotNetNuke.Entities.Portals; using DotNetNuke.Entities.Users; -using DotNetNuke.Services.Exceptions; -using DotNetNuke.Services.Localization; namespace Dnn.DynamicContent { @@ -43,12 +38,19 @@ public DataTypeManager(IDataContext dataContext) : base(dataContext) { } /// data type id. /// data type is null. /// dataType.Name is empty. + /// global data types can only be added by Super Users public int AddDataType(DataType dataType) { //Argument Contract Requires.PropertyNotNullOrEmpty(dataType, "Name"); - dataType.CreatedByUserId = UserController.Instance.GetCurrentUserInfo().UserID; + var currentUser = UserController.Instance.GetCurrentUserInfo(); + if (dataType.IsSystem && !currentUser.IsSuperUser) + { + throw new GlobalDataTypeSecurityException(); + } + + dataType.CreatedByUserId = currentUser.UserID; dataType.CreatedOnDate = DateUtilitiesManager.Instance.GetDatabaseTime(); Add(dataType); @@ -62,6 +64,7 @@ public int AddDataType(DataType dataType) /// The data type to delete. /// data type is null. /// data type id is less than 0. + /// global data types can only be deleted by Super Users public void DeleteDataType(DataType dataType) { //Argument Contract @@ -71,7 +74,7 @@ public void DeleteDataType(DataType dataType) if (dataType.IsSystem && !UserController.Instance.GetCurrentUserInfo().IsSuperUser) { - throw new SecurityException("Global Data Types can only delete by host user"); + throw new GlobalDataTypeSecurityException(); } using (DataContext) @@ -162,6 +165,7 @@ public IPagedList GetDataTypes(string searchTerm, int portalId, int pa /// data type is null. /// data type id is less than 0. /// dataType.Name is empty. + /// global data types can only be modified by Super Users public void UpdateDataType(DataType dataType, bool overrideWarning = false) { //Argument Contract @@ -170,12 +174,14 @@ public void UpdateDataType(DataType dataType, bool overrideWarning = false) Requires.PropertyNotNegative(dataType, "DataTypeId"); Requires.PropertyNotNullOrEmpty(dataType, "Name"); - if (dataType.IsSystem && !UserController.Instance.GetCurrentUserInfo().IsSuperUser) + var currentUser = UserController.Instance.GetCurrentUserInfo(); + + if (dataType.IsSystem && !currentUser.IsSuperUser) { - throw new SecurityException("Global Data Types can only update by host user"); + throw new GlobalDataTypeSecurityException(); } - dataType.LastModifiedByUserId = UserController.Instance.GetCurrentUserInfo().UserID; + dataType.LastModifiedByUserId = currentUser.UserID; dataType.LastModifiedOnDate = DateUtilitiesManager.Instance.GetDatabaseTime(); using (DataContext) diff --git a/DNN Platform/Dnn.DynamicContent/Dnn.DynamicContent.csproj b/DNN Platform/Dnn.DynamicContent/Dnn.DynamicContent.csproj index 930f797cdb6..356eceec167 100644 --- a/DNN Platform/Dnn.DynamicContent/Dnn.DynamicContent.csproj +++ b/DNN Platform/Dnn.DynamicContent/Dnn.DynamicContent.csproj @@ -73,6 +73,7 @@ + diff --git a/DNN Platform/Dnn.DynamicContent/Exceptions/GlobalDataTypeSecurityException.cs b/DNN Platform/Dnn.DynamicContent/Exceptions/GlobalDataTypeSecurityException.cs new file mode 100644 index 00000000000..04d50e8ce50 --- /dev/null +++ b/DNN Platform/Dnn.DynamicContent/Exceptions/GlobalDataTypeSecurityException.cs @@ -0,0 +1,20 @@ +// Copyright (c) DNN Software. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +using System.Security; + +namespace Dnn.DynamicContent.Exceptions +{ + /// + /// Global Data Type can be created, modified or deleted only by super users + /// + public class GlobalDataTypeSecurityException : SecurityException + { + public GlobalDataTypeSecurityException() + : base(string.Format( + DotNetNuke.Services.Localization.Localization.GetString("GlobalDataTypesSecurityException", + DotNetNuke.Services.Localization.Localization.ExceptionsResourceFile))) + { + } + } +} diff --git a/DNN Platform/Tests/Dnn.Tests.DynamicContent.UnitTests/DataTypeManagerTests.cs b/DNN Platform/Tests/Dnn.Tests.DynamicContent.UnitTests/DataTypeManagerTests.cs index fddc62eb9b7..6b0b00efaad 100644 --- a/DNN Platform/Tests/Dnn.Tests.DynamicContent.UnitTests/DataTypeManagerTests.cs +++ b/DNN Platform/Tests/Dnn.Tests.DynamicContent.UnitTests/DataTypeManagerTests.cs @@ -23,6 +23,7 @@ namespace Dnn.Tests.DynamicContent.UnitTests { + [TestFixture] class DataTypeManagerTests { private Mock _mockDataContext; @@ -56,6 +57,7 @@ public void TearDown() ContentTypeLocalizationManager.ClearInstance(); } + #region AddDataType tests [Test] public void AddDataType_Throws_On_Null_DataType() { @@ -75,6 +77,26 @@ public void AddDataType_Throws_On_Empty_Name_Property() //Act, Arrange Assert.Throws(() => dataTypeController.AddDataType(new DataType())); } + + [Test] + public void AddDataType_Throws_GlobalDataTypeSecurityException_When_GloabalDataTypeIsAddedByANonSuperUser() + { + //Arrange + var dataTypeController = new DataTypeManager(_mockDataContext.Object); + + var dataType = new DataType + { + DataTypeId = Constants.CONTENTTYPE_ValidDataTypeId, + Name = "DataType Name", + PortalId = Null.NullInteger // Portal -1 means global data type + }; + + //Act + var act = new TestDelegate(() => dataTypeController.AddDataType(dataType)); + + // Assert + Assert.Throws(act); + } [Test] public void AddDataType_Calls_Repository_Insert_On_Valid_Arguments() @@ -82,7 +104,7 @@ public void AddDataType_Calls_Repository_Insert_On_Valid_Arguments() //Arrange var dataTypeController = new DataTypeManager(_mockDataContext.Object); - var dataType = new DataType() { Name = "DataType1" }; + var dataType = GetValidDataType(); //Act // ReSharper disable once UnusedVariable @@ -101,7 +123,7 @@ public void AddDataType_Returns_ValidId_On_Valid_DataType() var dataTypeController = new DataTypeManager(_mockDataContext.Object); - var dataType = new DataType() { Name = "DataType1" }; + var dataType = GetValidDataType(); //Act int dataTypeId = dataTypeController.AddDataType(dataType); @@ -119,7 +141,7 @@ public void AddDataType_Sets_ValidId_On_Valid_DataType() var dataTypeController = new DataTypeManager(_mockDataContext.Object); - var dataType = new DataType() { Name = "DataType1" }; + var dataType = GetValidDataType(); //Act dataTypeController.AddDataType(dataType); @@ -142,7 +164,7 @@ public void AddDataType_Sets_Created_Audit_Info_On_Valid_DataType() var dataTypeController = new DataTypeManager(_mockDataContext.Object); - var dataType = new DataType() { Name = "DataType1" }; + var dataType = GetValidDataType(); //Act dataTypeController.AddDataType(dataType); @@ -150,7 +172,9 @@ public void AddDataType_Sets_Created_Audit_Info_On_Valid_DataType() //Assert Assert.AreEqual(userId, dataType.CreatedByUserId); } + #endregion + #region DeleteDataType tests [Test] public void DeleteDataType_Throws_On_Null_DataType() { @@ -173,12 +197,31 @@ public void DeleteDataType_Throws_On_Negative_DataTypeId() Assert.Throws(() => dataTypeController.DeleteDataType(dataType)); } + [Test] + public void DeleteDataType_Throws_GlobalDataTypeSecurityException_WhenGlobalDataTypeIsDeletedByNonSuperUser() + { + //Arrange + var dataTypeController = new DataTypeManager(_mockDataContext.Object); + + var dataType = new DataType + { + DataTypeId = Constants.CONTENTTYPE_ValidDataTypeId, + PortalId = Null.NullInteger // Portal -1 means global data type + }; + + //Act + var act = new TestDelegate(() => dataTypeController.DeleteDataType(dataType)); + + // Assert + Assert.Throws(act); + } + [Test] public void DeleteDataType_Calls_FieldDefinition_Repository_Find_On_Valid_DataTypeId() { //Arrange var dataTypeController = new DataTypeManager(_mockDataContext.Object); - var dataType = new DataType { DataTypeId = Constants.CONTENTTYPE_ValidDataTypeId }; + var dataType = GetValidDataType(); var mockLocalization = new Mock(); ContentTypeLocalizationManager.SetTestableInstance(mockLocalization.Object); @@ -186,18 +229,18 @@ public void DeleteDataType_Calls_FieldDefinition_Repository_Find_On_Valid_DataTy dataTypeController.DeleteDataType(dataType); //Assert - _mockFieldDefinitionRepository.Verify(r => r.Find(DataTypeManager.FindWhereDataTypeSql, Constants.CONTENTTYPE_ValidDataTypeId)); + _mockFieldDefinitionRepository.Verify(r => r.Find(DataTypeManager.FindWhereDataTypeSql, dataType.DataTypeId)); } [Test] public void DeleteDataType_Calls_Repository_Delete_If_DataType_UnUsed() { //Arrange + var dataType = GetValidDataType(); var dataTypeController = new DataTypeManager(_mockDataContext.Object); - _mockFieldDefinitionRepository.Setup(r => r.Find(DataTypeManager.FindWhereDataTypeSql, Constants.CONTENTTYPE_ValidDataTypeId)) + _mockFieldDefinitionRepository.Setup(r => r.Find(DataTypeManager.FindWhereDataTypeSql, dataType.DataTypeId)) .Returns(new List()); - var dataType = new DataType {DataTypeId = Constants.CONTENTTYPE_ValidDataTypeId}; var mockLocalization = new Mock(); ContentTypeLocalizationManager.SetTestableInstance(mockLocalization.Object); @@ -213,22 +256,24 @@ public void DeleteDataType_Calls_Repository_Delete_If_DataType_UnUsed() public void DeleteDataType_Throws_If_DataType_Used() { //Arrange + var dataType = GetValidDataType(); var dataTypeController = new DataTypeManager(_mockDataContext.Object); - _mockFieldDefinitionRepository.Setup(r => r.Find(DataTypeManager.FindWhereDataTypeSql, Constants.CONTENTTYPE_ValidDataTypeId)) + _mockFieldDefinitionRepository.Setup(r => r.Find(DataTypeManager.FindWhereDataTypeSql, dataType.DataTypeId)) .Returns(new List { new FieldDefinition() }); - var dataType = new DataType { DataTypeId = Constants.CONTENTTYPE_ValidDataTypeId }; //Act, Assert Assert.Throws(() => dataTypeController.DeleteDataType(dataType)); } + #endregion + #region GetDataType tests [Test] public void GetDataType_Calls_Repository_Get_With_PortalId() { //Arrange - var dataTypeId = Constants.CONTENTTYPE_ValidDataTypeId; - var portalId = Constants.PORTAL_ValidPortalId; + const int dataTypeId = Constants.CONTENTTYPE_ValidDataTypeId; + const int portalId = Constants.PORTAL_ValidPortalId; var dataTypeController = new DataTypeManager(_mockDataContext.Object); //Act @@ -491,7 +536,9 @@ public void GetDataTypes_Returns_Correct_DataTypes(int recordCount, int portalId Assert.IsFalse(dataTypes.HasNextPage); } } + #endregion + #region UpdateDataType tests [Test] public void UpdateDataType_Throws_On_Null_ContentType() { @@ -523,6 +570,26 @@ public void UpdateDataType_Throws_On_Negative_DataTypeId() Assert.Throws(() => dataTypeController.UpdateDataType(dataType)); } + + [Test] + public void UpdateDataType_Throws_GlobalDataTypeSecurityException_WhenGlobalDataTypeIsUpdatedByNonSuperUser() + { + //Arrange + var dataTypeController = new DataTypeManager(_mockDataContext.Object); + + var dataType = new DataType + { + DataTypeId = Constants.CONTENTTYPE_ValidDataTypeId, + Name = "New_Name", + PortalId = Null.NullInteger // Portal -1 means global data type + }; + + //Act + var act = new TestDelegate(() => dataTypeController.UpdateDataType(dataType)); + + // Assert + Assert.Throws(act); + } [Test] public void UpdateDataType_Calls_Repository_Update_If_DataType_Is_UnUsed() @@ -530,11 +597,7 @@ public void UpdateDataType_Calls_Repository_Update_If_DataType_Is_UnUsed() //Arrange var dataTypeController = new DataTypeManager(_mockDataContext.Object); - var dataType = new DataType - { - DataTypeId = Constants.CONTENTTYPE_UpdateDataTypeId, - Name = "New_Name" - }; + var dataType = GetValidDataType(); //Act dataTypeController.UpdateDataType(dataType); @@ -559,7 +622,8 @@ public void UpdateDataType_Calls_Repository_Update_If_DataType_Is_Used_But_No_Co var dataType = new DataType { DataTypeId = Constants.CONTENTTYPE_UpdateDataTypeId, - Name = "New_Name" + Name = "New_Name", + PortalId = 0 }; //Act @@ -586,7 +650,8 @@ public void UpdateDataType_Throws_If_DataType_Is_Used_And_Has_ContentItems() var dataType = new DataType { DataTypeId = dataTypeId, - Name = "New_Name" + Name = "New_Name", + PortalId = 0 }; //Act, Assert @@ -610,7 +675,8 @@ public void UpdateDataType_Calls_Repository_Update_If_DataType_Is_Used_And_Has_C var dataType = new DataType { DataTypeId = dataTypeId, - Name = "New_Name" + Name = "New_Name", + PortalId = 0 }; //Act @@ -642,7 +708,8 @@ public void UpdateDataType_Sets_Updated_Audit_Info_On_Valid_DataType() var dataType = new DataType { DataTypeId = Constants.CONTENTTYPE_UpdateDataTypeId, - Name = "New_Name" + Name = "New_Name", + PortalId = 0 }; //Act @@ -651,6 +718,17 @@ public void UpdateDataType_Sets_Updated_Audit_Info_On_Valid_DataType() //Assert Assert.AreEqual(userId, dataType.LastModifiedByUserId); } + #endregion + + private DataType GetValidDataType() + { + return new DataType + { + DataTypeId = Constants.CONTENTTYPE_ValidDataTypeId, + Name = "Data Type", + PortalId = 0 + }; + } private List GetValidDataTypes(int count) { @@ -658,7 +736,7 @@ private List GetValidDataTypes(int count) for (int i = 1; i <= count; i++) { - list.Add(new DataType() { DataTypeId = i, Name = String.Format("Name_{0}", i) }); + list.Add(new DataType { DataTypeId = i, Name = String.Format("Name_{0}", i) }); } return list; @@ -670,7 +748,7 @@ private List GetValidDataTypes(int count, int portalId) for (int i = 1; i <= count; i++) { - list.Add(new DataType() + list.Add(new DataType { DataTypeId = i, Name = String.Format("Name_{0}", i), diff --git a/Website/App_GlobalResources/Exceptions.resx b/Website/App_GlobalResources/Exceptions.resx index 2845556997f..58081b8e135 100644 --- a/Website/App_GlobalResources/Exceptions.resx +++ b/Website/App_GlobalResources/Exceptions.resx @@ -474,4 +474,7 @@ Asp.net membership update user failed. Possible Reason(s): you enabled requiresUniqueEmail in membership config, but there have different user(s) used same email address with current user. + + Global Data Types can only be created, modified or deleted by Super Users + \ No newline at end of file From 018b6a3f4ab86067ecd5c603035f6d86b901d9bd Mon Sep 17 00:00:00 2001 From: Francesco Rivola Date: Tue, 20 Oct 2015 12:27:12 +0200 Subject: [PATCH 3/3] DNN-7706 - Admin can delete any Data Type from the Data Types list view - added server side checks in add, update and delete content type to ensure that only Super Users can manage system content types - added corresponding unit tests Note: similar checks are still pending for Templates but will be done as part of an issue from the Architectural Review --- .../Dnn.DynamicContent/ContentTemplate.cs | 12 +- DNN Platform/Dnn.DynamicContent/DataType.cs | 15 +- .../Dnn.DynamicContent/DataTypeManager.cs | 32 ++- .../Dnn.DynamicContent.csproj | 3 +- .../Dnn.DynamicContent/DynamicContentType.cs | 10 +- .../DynamicContentTypeManager.cs | 42 +++- .../SystemContentTypeSecurityException.cs | 20 ++ ....cs => SystemDataTypeSecurityException.cs} | 6 +- .../Dnn.DynamicContent/IDataTypeManager.cs | 4 + .../IDynamicContentTypeManager.cs | 4 + .../DataTypeManagerTests.cs | 33 ++- .../DynamicContentTypeManagerTests.cs | 234 +++++++++++------- Website/App_GlobalResources/Exceptions.resx | 7 +- 13 files changed, 301 insertions(+), 121 deletions(-) create mode 100644 DNN Platform/Dnn.DynamicContent/Exceptions/SystemContentTypeSecurityException.cs rename DNN Platform/Dnn.DynamicContent/Exceptions/{GlobalDataTypeSecurityException.cs => SystemDataTypeSecurityException.cs} (78%) diff --git a/DNN Platform/Dnn.DynamicContent/ContentTemplate.cs b/DNN Platform/Dnn.DynamicContent/ContentTemplate.cs index 55d5e079fed..d781125b54e 100644 --- a/DNN Platform/Dnn.DynamicContent/ContentTemplate.cs +++ b/DNN Platform/Dnn.DynamicContent/ContentTemplate.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using System.Web.Caching; +using DotNetNuke.Common.Utilities; using DotNetNuke.ComponentModel.DataAnnotations; using DotNetNuke.Services.FileSystem; // ReSharper disable ConvertPropertyToExpressionBody @@ -56,8 +57,17 @@ public DynamicContentType ContentType /// public bool IsEditTemplate { get; set; } + /// + /// True if the content template is defined to be available for all portals, false otherwise + /// [IgnoreColumn] - public bool IsSystem { get { return (PortalId == -1); } } + public bool IsSystem + { + get + { + return PortalId == Null.NullInteger; + } + } /// /// The name of this diff --git a/DNN Platform/Dnn.DynamicContent/DataType.cs b/DNN Platform/Dnn.DynamicContent/DataType.cs index 6d92bc95d45..a5f501557b5 100644 --- a/DNN Platform/Dnn.DynamicContent/DataType.cs +++ b/DNN Platform/Dnn.DynamicContent/DataType.cs @@ -2,12 +2,10 @@ // Licensed under the MIT License. See LICENSE in the project root for license information. using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; using System.Threading; using System.Web.Caching; using Dnn.DynamicContent.Localization; +using DotNetNuke.Common.Utilities; using DotNetNuke.ComponentModel.DataAnnotations; // ReSharper disable ConvertPropertyToExpressionBody @@ -31,8 +29,17 @@ public DataType(int portalId) : base() public int DataTypeId { get; set; } + /// + /// True if the data type is defined to be available for all portals, false otherwise + /// [IgnoreColumn] - public bool IsSystem { get { return (PortalId == -1); } } + public bool IsSystem + { + get + { + return PortalId == Null.NullInteger; + } + } public string Name { get; set; } diff --git a/DNN Platform/Dnn.DynamicContent/DataTypeManager.cs b/DNN Platform/Dnn.DynamicContent/DataTypeManager.cs index f2c27896761..2774d699be9 100644 --- a/DNN Platform/Dnn.DynamicContent/DataTypeManager.cs +++ b/DNN Platform/Dnn.DynamicContent/DataTypeManager.cs @@ -38,7 +38,7 @@ public DataTypeManager(IDataContext dataContext) : base(dataContext) { } /// data type id. /// data type is null. /// dataType.Name is empty. - /// global data types can only be added by Super Users + /// system data types can only be added by Super Users public int AddDataType(DataType dataType) { //Argument Contract @@ -47,7 +47,7 @@ public int AddDataType(DataType dataType) var currentUser = UserController.Instance.GetCurrentUserInfo(); if (dataType.IsSystem && !currentUser.IsSuperUser) { - throw new GlobalDataTypeSecurityException(); + throw new SystemDataTypeSecurityException(); } dataType.CreatedByUserId = currentUser.UserID; @@ -64,7 +64,7 @@ public int AddDataType(DataType dataType) /// The data type to delete. /// data type is null. /// data type id is less than 0. - /// global data types can only be deleted by Super Users + /// system data types can only be deleted by Super Users public void DeleteDataType(DataType dataType) { //Argument Contract @@ -72,9 +72,16 @@ public void DeleteDataType(DataType dataType) Requires.PropertyNotNull(dataType, "DataTypeId"); Requires.PropertyNotNegative(dataType, "DataTypeId"); - if (dataType.IsSystem && !UserController.Instance.GetCurrentUserInfo().IsSuperUser) + var storedDataType = GetDataType(dataType.DataTypeId, dataType.PortalId, true); + if (storedDataType == null) { - throw new GlobalDataTypeSecurityException(); + // TODO: add DataTypeDoesNotExistException here + return; + } + + if (storedDataType.IsSystem && !UserController.Instance.GetCurrentUserInfo().IsSuperUser) + { + throw new SystemDataTypeSecurityException(); } using (DataContext) @@ -165,7 +172,7 @@ public IPagedList GetDataTypes(string searchTerm, int portalId, int pa /// data type is null. /// data type id is less than 0. /// dataType.Name is empty. - /// global data types can only be modified by Super Users + /// system data types can only be modified by Super Users public void UpdateDataType(DataType dataType, bool overrideWarning = false) { //Argument Contract @@ -174,11 +181,18 @@ public void UpdateDataType(DataType dataType, bool overrideWarning = false) Requires.PropertyNotNegative(dataType, "DataTypeId"); Requires.PropertyNotNullOrEmpty(dataType, "Name"); - var currentUser = UserController.Instance.GetCurrentUserInfo(); - if (dataType.IsSystem && !currentUser.IsSuperUser) + var storedDataType = GetDataType(dataType.DataTypeId, dataType.PortalId, true); + if (storedDataType == null) + { + // TODO: add DataTypeDoesNotExistException here + return; + } + + var currentUser = UserController.Instance.GetCurrentUserInfo(); + if (storedDataType.IsSystem && !currentUser.IsSuperUser) { - throw new GlobalDataTypeSecurityException(); + throw new SystemDataTypeSecurityException(); } dataType.LastModifiedByUserId = currentUser.UserID; diff --git a/DNN Platform/Dnn.DynamicContent/Dnn.DynamicContent.csproj b/DNN Platform/Dnn.DynamicContent/Dnn.DynamicContent.csproj index 356eceec167..210f94e015b 100644 --- a/DNN Platform/Dnn.DynamicContent/Dnn.DynamicContent.csproj +++ b/DNN Platform/Dnn.DynamicContent/Dnn.DynamicContent.csproj @@ -73,7 +73,8 @@ - + + diff --git a/DNN Platform/Dnn.DynamicContent/DynamicContentType.cs b/DNN Platform/Dnn.DynamicContent/DynamicContentType.cs index 3b075c15f63..4580658bfc7 100644 --- a/DNN Platform/Dnn.DynamicContent/DynamicContentType.cs +++ b/DNN Platform/Dnn.DynamicContent/DynamicContentType.cs @@ -68,10 +68,16 @@ public IList FieldDefinitions public bool IsDynamic { get; set; } /// - /// A flag that indicates whether the Content Type is a system type + /// A flag that indicates whether the Content Type is a global type available cross all portals /// [IgnoreColumn] - public bool IsSystem { get { return (PortalId == -1); } } + public bool IsSystem + { + get + { + return PortalId == Null.NullInteger; + } + } /// /// Gets or sets the name of the ContentType. diff --git a/DNN Platform/Dnn.DynamicContent/DynamicContentTypeManager.cs b/DNN Platform/Dnn.DynamicContent/DynamicContentTypeManager.cs index 1ccc7a18dfe..102a6fe6d84 100644 --- a/DNN Platform/Dnn.DynamicContent/DynamicContentTypeManager.cs +++ b/DNN Platform/Dnn.DynamicContent/DynamicContentTypeManager.cs @@ -2,9 +2,9 @@ // Licensed under the MIT License. See LICENSE in the project root for license information. using System; -using System.Collections.Generic; using System.Linq; using Dnn.DynamicContent.Common; +using Dnn.DynamicContent.Exceptions; using Dnn.DynamicContent.Localization; using DotNetNuke.Collections; using DotNetNuke.Common; @@ -35,12 +35,19 @@ public DynamicContentTypeManager(IDataContext dataContext) : base(dataContext) { /// content type id. /// content type is null. /// contentType.ContentType is empty. + /// system content types can only be added by Super Users public int AddContentType(DynamicContentType contentType) { //Argument Contract Requires.PropertyNotNullOrEmpty(contentType, "Name"); - contentType.CreatedByUserId = UserController.Instance.GetCurrentUserInfo().UserID; + var currentUser = UserController.Instance.GetCurrentUserInfo(); + if (contentType.IsSystem && !currentUser.IsSuperUser) + { + throw new SystemContentTypeSecurityException(); + } + + contentType.CreatedByUserId = currentUser.UserID; contentType.CreatedOnDate = DateUtilitiesManager.Instance.GetDatabaseTime(); Add(contentType); @@ -68,10 +75,24 @@ public int AddContentType(DynamicContentType contentType) /// Type of the content. /// content type is null. /// content type id is less than 0. + /// system content types can only be deleted by Super Users public void DeleteContentType(DynamicContentType contentType) { Requires.NotNull(contentType); Requires.PropertyNotNegative(contentType, "ContentTypeId"); + + var storedContentType = GetContentType(contentType.ContentTypeId, contentType.PortalId, true); + if (storedContentType == null) + { + // TODO: add ContentTypeDoesNotExistException here + return; + } + + var currentUser = UserController.Instance.GetCurrentUserInfo(); + if (storedContentType.IsSystem && !currentUser.IsSuperUser) + { + throw new SystemContentTypeSecurityException(); + } //Delete Field Definitions foreach (var definition in contentType.FieldDefinitions) @@ -160,12 +181,27 @@ public IPagedList GetContentTypes(string searchTerm, int por /// content type is null. /// content type id is less than 0. /// contentType.ContentType is empty. + /// system content types can only be modified by Super Users public void UpdateContentType(DynamicContentType contentType) { //Argument Contract Requires.PropertyNotNullOrEmpty(contentType, "Name"); + Requires.PropertyNotNegative(contentType, "ContentTypeId"); + + var storedContentType = GetContentType(contentType.ContentTypeId, contentType.PortalId, true); + if (storedContentType == null) + { + // TODO: add ContentTypeDoesNotExistException here + return; + } + + var currentUser = UserController.Instance.GetCurrentUserInfo(); + if (storedContentType.IsSystem && !currentUser.IsSuperUser) + { + throw new SystemContentTypeSecurityException(); + } - contentType.LastModifiedByUserId = UserController.Instance.GetCurrentUserInfo().UserID; + contentType.LastModifiedByUserId = currentUser.UserID; contentType.LastModifiedOnDate = DateUtilitiesManager.Instance.GetDatabaseTime(); Update(contentType); diff --git a/DNN Platform/Dnn.DynamicContent/Exceptions/SystemContentTypeSecurityException.cs b/DNN Platform/Dnn.DynamicContent/Exceptions/SystemContentTypeSecurityException.cs new file mode 100644 index 00000000000..5f80a2586cf --- /dev/null +++ b/DNN Platform/Dnn.DynamicContent/Exceptions/SystemContentTypeSecurityException.cs @@ -0,0 +1,20 @@ +// Copyright (c) DNN Software. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +using System.Security; + +namespace Dnn.DynamicContent.Exceptions +{ + /// + /// Global Content Type can be created, modified or deleted only by super users + /// + public class SystemContentTypeSecurityException : SecurityException + { + public SystemContentTypeSecurityException() + : base(string.Format( + DotNetNuke.Services.Localization.Localization.GetString("SystemContentTypesSecurityException", + DotNetNuke.Services.Localization.Localization.ExceptionsResourceFile))) + { + } + } +} diff --git a/DNN Platform/Dnn.DynamicContent/Exceptions/GlobalDataTypeSecurityException.cs b/DNN Platform/Dnn.DynamicContent/Exceptions/SystemDataTypeSecurityException.cs similarity index 78% rename from DNN Platform/Dnn.DynamicContent/Exceptions/GlobalDataTypeSecurityException.cs rename to DNN Platform/Dnn.DynamicContent/Exceptions/SystemDataTypeSecurityException.cs index 04d50e8ce50..7a7869ffb24 100644 --- a/DNN Platform/Dnn.DynamicContent/Exceptions/GlobalDataTypeSecurityException.cs +++ b/DNN Platform/Dnn.DynamicContent/Exceptions/SystemDataTypeSecurityException.cs @@ -8,11 +8,11 @@ namespace Dnn.DynamicContent.Exceptions /// /// Global Data Type can be created, modified or deleted only by super users /// - public class GlobalDataTypeSecurityException : SecurityException + public class SystemDataTypeSecurityException : SecurityException { - public GlobalDataTypeSecurityException() + public SystemDataTypeSecurityException() : base(string.Format( - DotNetNuke.Services.Localization.Localization.GetString("GlobalDataTypesSecurityException", + DotNetNuke.Services.Localization.Localization.GetString("SystemDataTypesSecurityException", DotNetNuke.Services.Localization.Localization.ExceptionsResourceFile))) { } diff --git a/DNN Platform/Dnn.DynamicContent/IDataTypeManager.cs b/DNN Platform/Dnn.DynamicContent/IDataTypeManager.cs index b406208ca82..62a5ca0c884 100644 --- a/DNN Platform/Dnn.DynamicContent/IDataTypeManager.cs +++ b/DNN Platform/Dnn.DynamicContent/IDataTypeManager.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. See LICENSE in the project root for license information. using System.Linq; +using Dnn.DynamicContent.Exceptions; using DotNetNuke.Collections; namespace Dnn.DynamicContent @@ -13,12 +14,14 @@ public interface IDataTypeManager /// /// The data type to add. /// data type id. + /// system data types can only be added by Super Users int AddDataType(DataType dataType); /// /// Deletes the data type for use with Structured(Dynamic) Content Types. /// /// The data type to delete. + /// system data types can only be deleted by Super Users void DeleteDataType(DataType dataType); /// @@ -60,6 +63,7 @@ public interface IDataTypeManager /// data type is null. /// data type id is less than 0. /// dataType.Name is empty. + /// system data types can only be modified by Super Users void UpdateDataType(DataType dataType, bool overrideWarning = false); } } diff --git a/DNN Platform/Dnn.DynamicContent/IDynamicContentTypeManager.cs b/DNN Platform/Dnn.DynamicContent/IDynamicContentTypeManager.cs index 5eb076f4b6f..f88cad8112e 100644 --- a/DNN Platform/Dnn.DynamicContent/IDynamicContentTypeManager.cs +++ b/DNN Platform/Dnn.DynamicContent/IDynamicContentTypeManager.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. See LICENSE in the project root for license information. using System.Linq; +using Dnn.DynamicContent.Exceptions; using DotNetNuke.Collections; namespace Dnn.DynamicContent @@ -15,6 +16,7 @@ public interface IDynamicContentTypeManager /// content type id. /// content type is null. /// contentType.ContentType is empty. + /// system content types can only be added by Super Users int AddContentType(DynamicContentType contentType); /// @@ -23,6 +25,7 @@ public interface IDynamicContentTypeManager /// Type of the content. /// content type is null. /// content type id is less than 0. + /// system content types can only be deleted by Super Users void DeleteContentType(DynamicContentType contentType); /// @@ -63,6 +66,7 @@ public interface IDynamicContentTypeManager /// content type is null. /// content type id is less than 0. /// contentType.ContentType is empty. + /// system content types can only be modified by Super Users void UpdateContentType(DynamicContentType contentType); } } diff --git a/DNN Platform/Tests/Dnn.Tests.DynamicContent.UnitTests/DataTypeManagerTests.cs b/DNN Platform/Tests/Dnn.Tests.DynamicContent.UnitTests/DataTypeManagerTests.cs index 6b0b00efaad..65a6a1e7946 100644 --- a/DNN Platform/Tests/Dnn.Tests.DynamicContent.UnitTests/DataTypeManagerTests.cs +++ b/DNN Platform/Tests/Dnn.Tests.DynamicContent.UnitTests/DataTypeManagerTests.cs @@ -79,7 +79,7 @@ public void AddDataType_Throws_On_Empty_Name_Property() } [Test] - public void AddDataType_Throws_GlobalDataTypeSecurityException_When_GloabalDataTypeIsAddedByANonSuperUser() + public void AddDataType_Throws_SystemDataTypeSecurityException_When_SystemDataTypeIsAddedByANonSuperUser() { //Arrange var dataTypeController = new DataTypeManager(_mockDataContext.Object); @@ -95,7 +95,7 @@ public void AddDataType_Throws_GlobalDataTypeSecurityException_When_GloabalDataT var act = new TestDelegate(() => dataTypeController.AddDataType(dataType)); // Assert - Assert.Throws(act); + Assert.Throws(act); } [Test] @@ -198,7 +198,7 @@ public void DeleteDataType_Throws_On_Negative_DataTypeId() } [Test] - public void DeleteDataType_Throws_GlobalDataTypeSecurityException_WhenGlobalDataTypeIsDeletedByNonSuperUser() + public void DeleteDataType_Throws_SystemDataTypeSecurityException_WhenSystemDataTypeIsDeletedByNonSuperUser() { //Arrange var dataTypeController = new DataTypeManager(_mockDataContext.Object); @@ -208,12 +208,14 @@ public void DeleteDataType_Throws_GlobalDataTypeSecurityException_WhenGlobalData DataTypeId = Constants.CONTENTTYPE_ValidDataTypeId, PortalId = Null.NullInteger // Portal -1 means global data type }; + _mockDataTypeRepository.Setup(r => r.Get(dataType.PortalId)).Returns(new[] { dataType }); //Act var act = new TestDelegate(() => dataTypeController.DeleteDataType(dataType)); // Assert - Assert.Throws(act); + Assert.Throws(act); + _mockDataTypeRepository.VerifyAll(); } [Test] @@ -224,12 +226,14 @@ public void DeleteDataType_Calls_FieldDefinition_Repository_Find_On_Valid_DataTy var dataType = GetValidDataType(); var mockLocalization = new Mock(); ContentTypeLocalizationManager.SetTestableInstance(mockLocalization.Object); + _mockDataTypeRepository.Setup(r => r.Get(dataType.PortalId)).Returns(new []{dataType}); //Act dataTypeController.DeleteDataType(dataType); //Assert _mockFieldDefinitionRepository.Verify(r => r.Find(DataTypeManager.FindWhereDataTypeSql, dataType.DataTypeId)); + _mockDataTypeRepository.VerifyAll(); } [Test] @@ -240,6 +244,7 @@ public void DeleteDataType_Calls_Repository_Delete_If_DataType_UnUsed() var dataTypeController = new DataTypeManager(_mockDataContext.Object); _mockFieldDefinitionRepository.Setup(r => r.Find(DataTypeManager.FindWhereDataTypeSql, dataType.DataTypeId)) .Returns(new List()); + _mockDataTypeRepository.Setup(r => r.Get(dataType.PortalId)).Returns(new[] { dataType }); var mockLocalization = new Mock(); @@ -250,6 +255,7 @@ public void DeleteDataType_Calls_Repository_Delete_If_DataType_UnUsed() //Assert _mockDataTypeRepository.Verify(r => r.Delete(dataType)); + _mockDataTypeRepository.VerifyAll(); } [Test] @@ -260,10 +266,12 @@ public void DeleteDataType_Throws_If_DataType_Used() var dataTypeController = new DataTypeManager(_mockDataContext.Object); _mockFieldDefinitionRepository.Setup(r => r.Find(DataTypeManager.FindWhereDataTypeSql, dataType.DataTypeId)) .Returns(new List { new FieldDefinition() }); + _mockDataTypeRepository.Setup(r => r.Get(dataType.PortalId)).Returns(new[] { dataType }); //Act, Assert Assert.Throws(() => dataTypeController.DeleteDataType(dataType)); + _mockDataTypeRepository.VerifyAll(); } #endregion @@ -572,7 +580,7 @@ public void UpdateDataType_Throws_On_Negative_DataTypeId() } [Test] - public void UpdateDataType_Throws_GlobalDataTypeSecurityException_WhenGlobalDataTypeIsUpdatedByNonSuperUser() + public void UpdateDataType_Throws_SystemDataTypeSecurityException_WhenSystemDataTypeIsUpdatedByNonSuperUser() { //Arrange var dataTypeController = new DataTypeManager(_mockDataContext.Object); @@ -583,12 +591,14 @@ public void UpdateDataType_Throws_GlobalDataTypeSecurityException_WhenGlobalData Name = "New_Name", PortalId = Null.NullInteger // Portal -1 means global data type }; + _mockDataTypeRepository.Setup(r => r.Get(dataType.PortalId)).Returns(new[] { dataType }); //Act var act = new TestDelegate(() => dataTypeController.UpdateDataType(dataType)); // Assert - Assert.Throws(act); + Assert.Throws(act); + _mockDataTypeRepository.VerifyAll(); } [Test] @@ -596,14 +606,15 @@ public void UpdateDataType_Calls_Repository_Update_If_DataType_Is_UnUsed() { //Arrange var dataTypeController = new DataTypeManager(_mockDataContext.Object); - var dataType = GetValidDataType(); + _mockDataTypeRepository.Setup(r => r.Get(dataType.PortalId)).Returns(new[] { dataType }); //Act dataTypeController.UpdateDataType(dataType); //Assert _mockDataTypeRepository.Verify(r => r.Update(dataType)); + _mockDataTypeRepository.VerifyAll(); } [Test] @@ -625,12 +636,14 @@ public void UpdateDataType_Calls_Repository_Update_If_DataType_Is_Used_But_No_Co Name = "New_Name", PortalId = 0 }; + _mockDataTypeRepository.Setup(r => r.Get(dataType.PortalId)).Returns(new[] { dataType }); //Act dataTypeController.UpdateDataType(dataType); //Assert _mockDataTypeRepository.Verify(r => r.Update(dataType)); + _mockDataTypeRepository.VerifyAll(); } [Test] @@ -653,9 +666,11 @@ public void UpdateDataType_Throws_If_DataType_Is_Used_And_Has_ContentItems() Name = "New_Name", PortalId = 0 }; + _mockDataTypeRepository.Setup(r => r.Get(dataType.PortalId)).Returns(new[] { dataType }); //Act, Assert Assert.Throws(() => dataTypeController.UpdateDataType(dataType)); + _mockDataTypeRepository.VerifyAll(); } [Test] @@ -678,12 +693,14 @@ public void UpdateDataType_Calls_Repository_Update_If_DataType_Is_Used_And_Has_C Name = "New_Name", PortalId = 0 }; + _mockDataTypeRepository.Setup(r => r.Get(dataType.PortalId)).Returns(new[] { dataType }); //Act dataTypeController.UpdateDataType(dataType, true); //Assert _mockDataTypeRepository.Verify(r => r.Update(dataType)); + _mockDataTypeRepository.VerifyAll(); } [Test] @@ -711,12 +728,14 @@ public void UpdateDataType_Sets_Updated_Audit_Info_On_Valid_DataType() Name = "New_Name", PortalId = 0 }; + _mockDataTypeRepository.Setup(r => r.Get(dataType.PortalId)).Returns(new[] { dataType }); //Act dataTypeController.UpdateDataType(dataType); //Assert Assert.AreEqual(userId, dataType.LastModifiedByUserId); + _mockDataTypeRepository.VerifyAll(); } #endregion diff --git a/DNN Platform/Tests/Dnn.Tests.DynamicContent.UnitTests/DynamicContentTypeManagerTests.cs b/DNN Platform/Tests/Dnn.Tests.DynamicContent.UnitTests/DynamicContentTypeManagerTests.cs index 3ad46b7e981..6cb3a099b22 100644 --- a/DNN Platform/Tests/Dnn.Tests.DynamicContent.UnitTests/DynamicContentTypeManagerTests.cs +++ b/DNN Platform/Tests/Dnn.Tests.DynamicContent.UnitTests/DynamicContentTypeManagerTests.cs @@ -5,11 +5,11 @@ using System.Collections.Generic; using System.Linq; using Dnn.DynamicContent; +using Dnn.DynamicContent.Exceptions; using Dnn.DynamicContent.Localization; using DotNetNuke.Collections; using DotNetNuke.Common.Utilities; using DotNetNuke.Data; -using DotNetNuke.Entities.Content; using DotNetNuke.Entities.Users; using DotNetNuke.Services.Cache; using DotNetNuke.Tests.Utilities; @@ -26,10 +26,8 @@ public class DynamicContentTypeManagerTests private Mock> _mockContentTypeRepository; private Mock _mockFieldDefinitionController; private Mock _mockContentTemplateController; - private Mock _mockCache; - private string _contentTypeCacheKey; - + [SetUp] public void SetUp() { @@ -37,8 +35,6 @@ public void SetUp() _mockCache = MockComponentProvider.CreateNew(); MockComponentProvider.CreateDataProvider().Setup(c => c.GetProviderPath()).Returns(String.Empty); - _contentTypeCacheKey = CachingProvider.GetCacheKey(DataCache.ContentTypesCacheKey); - _mockDataContext = new Mock(); _mockContentTypeRepository = new Mock>(); @@ -74,6 +70,7 @@ public void Constructor_Throws_On_Null_DataContext() Assert.Throws(() => new DynamicContentTypeManager(dataContent)); } + #region AddContentType tests [Test] public void AddContentType_Throws_On_Null_ContentType() { @@ -94,13 +91,28 @@ public void AddContentType_Throws_On_Empty_ContentType_Property() Assert.Throws(() => contentTypeController.AddContentType(new DynamicContentType())); } + [Test] + public void + AddContentType_Should_Thrown_SystemContentTypeSecurityException_When_SystemContentTypeIsAddedByNonSuperUser() + { + // Arrange + var contentTypeController = new DynamicContentTypeManager(_mockDataContext.Object); + var contentType = GetValidContentType(); + contentType.PortalId = Null.NullInteger; // Means system content type + + // Act + var act = new TestDelegate(() => contentTypeController.AddContentType(contentType)); + + // Assert + Assert.Throws(act); + } + [Test] public void AddContentType_Calls_Repository_Insert_On_Valid_Arguments() { //Arrange var contentTypeController = new DynamicContentTypeManager(_mockDataContext.Object); - - var contentType = new DynamicContentType { Name = Constants.CONTENTTYPE_ValidContentType }; + var contentType = GetValidNewContentType(); //Act // ReSharper disable once UnusedVariable @@ -114,12 +126,11 @@ public void AddContentType_Calls_Repository_Insert_On_Valid_Arguments() public void AddContentType_Returns_ValidId_On_Valid_ContentType() { //Arrange + var contentType = GetValidNewContentType(); _mockContentTypeRepository.Setup(r => r.Insert(It.IsAny())) .Callback((DynamicContentType ct) => ct.ContentTypeId = Constants.CONTENTTYPE_AddContentTypeId); - var contentTypeController = new DynamicContentTypeManager(_mockDataContext.Object); - var contentType = new DynamicContentType { Name = Constants.CONTENTTYPE_ValidContentType }; //Act int contentTypeId = contentTypeController.AddContentType(contentType); @@ -132,12 +143,12 @@ public void AddContentType_Returns_ValidId_On_Valid_ContentType() public void AddContentType_Sets_ValidId_On_Valid_ContentType() { //Arrange + var contentType = GetValidNewContentType(); _mockContentTypeRepository.Setup(r => r.Insert(It.IsAny())) .Callback((DynamicContentType ct) => ct.ContentTypeId = Constants.CONTENTTYPE_AddContentTypeId); var contentTypeController = new DynamicContentTypeManager(_mockDataContext.Object); - var contentType = new DynamicContentType { Name = Constants.CONTENTTYPE_ValidContentType }; //Act contentTypeController.AddContentType(contentType); @@ -150,12 +161,12 @@ public void AddContentType_Sets_ValidId_On_Valid_ContentType() public void AddContentType_Adds_FieldDefinitions_On_Valid_ContentType() { //Arrange + var contentType = GetValidNewContentType(); _mockContentTypeRepository.Setup(r => r.Insert(It.IsAny())) .Callback((DynamicContentType ct) => ct.ContentTypeId = Constants.CONTENTTYPE_AddContentTypeId); var contentTypeController = new DynamicContentTypeManager(_mockDataContext.Object); - var contentType = new DynamicContentType { Name = Constants.CONTENTTYPE_ValidContentType }; var fieldDefinitionCount = 5; for (int i = 0; i < fieldDefinitionCount; i++) @@ -174,14 +185,12 @@ public void AddContentType_Adds_FieldDefinitions_On_Valid_ContentType() public void AddContentType_Sets_ContentTypeId_Property_Of_New_FieldDefinitions_On_Valid_ContentType() { //Arrange - var contentTypeId = Constants.CONTENTTYPE_AddContentTypeId; + var contentType = GetValidNewContentType(); _mockContentTypeRepository.Setup(r => r.Insert(It.IsAny())) - .Callback((DynamicContentType ct) => ct.ContentTypeId = contentTypeId); + .Callback((DynamicContentType ct) => ct.ContentTypeId = Constants.CONTENTTYPE_AddContentTypeId); var contentTypeController = new DynamicContentTypeManager(_mockDataContext.Object); - - var contentType = new DynamicContentType { Name = Constants.CONTENTTYPE_ValidContentType }; - + var fieldDefinitionCount = 5; for (int i = 0; i < fieldDefinitionCount; i++) { @@ -194,7 +203,7 @@ public void AddContentType_Sets_ContentTypeId_Property_Of_New_FieldDefinitions_O //Assert foreach (var field in contentType.FieldDefinitions) { - Assert.AreEqual(contentTypeId, field.ContentTypeId); + Assert.AreEqual(contentType.ContentTypeId, field.ContentTypeId); } } @@ -202,12 +211,12 @@ public void AddContentType_Sets_ContentTypeId_Property_Of_New_FieldDefinitions_O public void AddContentType_Adds_ContentTemplates_On_Valid_ContentType() { //Arrange + var contentType = GetValidNewContentType(); _mockContentTypeRepository.Setup(r => r.Insert(It.IsAny())) .Callback((DynamicContentType ct) => ct.ContentTypeId = Constants.CONTENTTYPE_AddContentTypeId); var contentTypeController = new DynamicContentTypeManager(_mockDataContext.Object); - var contentType = new DynamicContentType { Name = Constants.CONTENTTYPE_ValidContentType }; var contentTemplateCount = 5; for (int i = 0; i < contentTemplateCount; i++) @@ -232,7 +241,7 @@ public void AddContentType_Sets_ContentTypeId_Property_Of_New_ContentTemplates_O var contentTypeController = new DynamicContentTypeManager(_mockDataContext.Object); - var contentType = new DynamicContentType { Name = Constants.CONTENTTYPE_ValidContentType }; + var contentType = GetValidNewContentType(); var contentTemplateCount = 5; for (int i = 0; i < contentTemplateCount; i++) @@ -264,7 +273,7 @@ public void AddContentType_Sets_CreatedByUserId_Property_On_Valid_ContentType() var contentTypeController = new DynamicContentTypeManager(_mockDataContext.Object); - var contentType = new DynamicContentType { Name = Constants.CONTENTTYPE_ValidContentType }; + var contentType = GetValidNewContentType(); //Act contentTypeController.AddContentType(contentType); @@ -272,8 +281,9 @@ public void AddContentType_Sets_CreatedByUserId_Property_On_Valid_ContentType() //Assert Assert.AreEqual(userId, contentType.CreatedByUserId); } + #endregion - + #region DeleteContentType tests [Test] public void DeleteContentType_Throws_On_Null_ContentType() { @@ -299,17 +309,32 @@ public void DeleteContentType_Throws_On_Negative_ContentTypeId() //Act, Arrange Assert.Throws(() => contentTypeController.DeleteContentType(contentType)); } + + [Test] + public void + DeleteContentType_Should_Thrown_SystemContentTypeSecurityException_When_SystemContentTypeIsDeletedByNonSuperUser() + { + // Arrange + var contentTypeController = new DynamicContentTypeManager(_mockDataContext.Object); + var contentType = GetValidContentType(); + contentType.PortalId = Null.NullInteger; // Means system content type + _mockContentTypeRepository.Setup(r => r.Get(contentType.PortalId)).Returns(new[] {contentType}); + + // Act + var act = new TestDelegate(() => contentTypeController.DeleteContentType(contentType)); + + // Assert + Assert.Throws(act); + _mockContentTypeRepository.VerifyAll(); + } [Test] public void DeleteContentType_Calls_Repository_Delete_On_Valid_ContentTypeId() { //Arrange var contentTypeController = new DynamicContentTypeManager(_mockDataContext.Object); - var contentType = new DynamicContentType - { - Name = Constants.CONTENTTYPE_ValidContentType, - ContentTypeId = Constants.CONTENTTYPE_ValidContentTypeId - }; + var contentType = GetValidContentType(); + _mockContentTypeRepository.Setup(r => r.Get(contentType.PortalId)).Returns(new[] { contentType }); var mockLocalization = new Mock(); ContentTypeLocalizationManager.SetTestableInstance(mockLocalization.Object); @@ -319,6 +344,7 @@ public void DeleteContentType_Calls_Repository_Delete_On_Valid_ContentTypeId() //Assert _mockContentTypeRepository.Verify(r => r.Delete(contentType)); + _mockContentTypeRepository.VerifyAll(); } [Test] @@ -326,11 +352,8 @@ public void DeleteContentType_Deletes_FieldDefinitions_On_Valid_ContentTypeId() { //Arrange var contentTypeController = new DynamicContentTypeManager(_mockDataContext.Object); - var contentType = new DynamicContentType - { - Name = Constants.CONTENTTYPE_ValidContentType, - ContentTypeId = Constants.CONTENTTYPE_ValidContentTypeId - }; + var contentType = GetValidContentType(); + _mockContentTypeRepository.Setup(r => r.Get(contentType.PortalId)).Returns(new[] { contentType }); var mockLocalization = new Mock(); ContentTypeLocalizationManager.SetTestableInstance(mockLocalization.Object); @@ -346,6 +369,7 @@ public void DeleteContentType_Deletes_FieldDefinitions_On_Valid_ContentTypeId() //Assert _mockFieldDefinitionController.Verify(f => f.DeleteFieldDefinition(It.IsAny()), Times.Exactly(fieldDefinitionCount)); + _mockContentTypeRepository.VerifyAll(); } @@ -354,11 +378,8 @@ public void DeleteContentType_Deletes_Templates_On_Valid_ContentTypeId() { //Arrange var contentTypeController = new DynamicContentTypeManager(_mockDataContext.Object); - var contentType = new DynamicContentType - { - Name = Constants.CONTENTTYPE_ValidContentType, - ContentTypeId = Constants.CONTENTTYPE_ValidContentTypeId - }; + var contentType = GetValidContentType(); + _mockContentTypeRepository.Setup(r => r.Get(contentType.PortalId)).Returns(new[] { contentType }); var mockLocalization = new Mock(); ContentTypeLocalizationManager.SetTestableInstance(mockLocalization.Object); @@ -374,8 +395,11 @@ public void DeleteContentType_Deletes_Templates_On_Valid_ContentTypeId() //Assert _mockContentTemplateController.Verify(ct => ct.DeleteContentTemplate(It.IsAny()), Times.Exactly(contentTemplateCount)); + _mockContentTypeRepository.VerifyAll(); } + #endregion + #region GetContentTypes tests [Test] public void GetContentTypes_Calls_Repository_Get_With_PortalId() { @@ -516,7 +540,9 @@ public void GetContentTypes_Returns_Correct_ContentTypes(int recordCount, int po Assert.IsFalse(contentTypes.HasNextPage); } } + #endregion + #region UpdateContentType tests [Test] public void UpdateContentType_Throws_On_Null_ContentType() { @@ -552,6 +578,24 @@ public void UpdateContentType_Throws_On_Negative_ContentTypeId() Assert.Throws(() => contentTypeController.UpdateContentType(contentType)); } + + [Test] + public void + UpdateContentType_Should_Thrown_SystemContentTypeSecurityException_When_SystemContentTypeIsModifiedByNonSuperUser() + { + // Arrange + var contentTypeController = new DynamicContentTypeManager(_mockDataContext.Object); + var contentType = GetValidUpdateContentType(); + contentType.PortalId = Null.NullInteger; // Means system content type + _mockContentTypeRepository.Setup(r => r.Get(contentType.PortalId)).Returns(new[] { contentType }); + + // Act + var act = new TestDelegate(() => contentTypeController.UpdateContentType(contentType)); + + // Assert + Assert.Throws(act); + _mockContentTypeRepository.VerifyAll(); + } [Test] public void UpdateContentType_Calls_Repository_Update_On_Valid_ContentType() @@ -559,17 +603,15 @@ public void UpdateContentType_Calls_Repository_Update_On_Valid_ContentType() //Arrange var contentTypeController = new DynamicContentTypeManager(_mockDataContext.Object); - var contentType = new DynamicContentType - { - ContentTypeId = Constants.CONTENTTYPE_UpdateContentTypeId, - Name = Constants.CONTENTTYPE_UpdateContentType - }; + var contentType = GetValidUpdateContentType(); + _mockContentTypeRepository.Setup(r => r.Get(contentType.PortalId)).Returns(new[] { contentType }); //Act contentTypeController.UpdateContentType(contentType); //Assert _mockContentTypeRepository.Verify(r => r.Update(contentType)); + _mockContentTypeRepository.VerifyAll(); } [Test] @@ -578,13 +620,10 @@ public void UpdateContentType_Adds_New_FieldDefinitions_On_Valid_ContentType() //Arrange var contentTypeController = new DynamicContentTypeManager(_mockDataContext.Object); - var contentType = new DynamicContentType - { - ContentTypeId = Constants.CONTENTTYPE_UpdateContentTypeId, - Name = Constants.CONTENTTYPE_UpdateContentType - }; + var contentType = GetValidUpdateContentType(); + _mockContentTypeRepository.Setup(r => r.Get(contentType.PortalId)).Returns(new[] { contentType }); - var fieldDefinitionCount = 5; + const int fieldDefinitionCount = 5; for (int i = 0; i < fieldDefinitionCount; i++) { contentType.FieldDefinitions.Add(new FieldDefinition()); @@ -595,6 +634,7 @@ public void UpdateContentType_Adds_New_FieldDefinitions_On_Valid_ContentType() //Assert _mockFieldDefinitionController.Verify(fd => fd.AddFieldDefinition(It.IsAny()), Times.Exactly(fieldDefinitionCount)); + _mockContentTypeRepository.VerifyAll(); } [Test] @@ -603,14 +643,10 @@ public void UpdateContentType_Sets_ContentTypeId_Property_Of_New_New_FieldDefini //Arrange var contentTypeController = new DynamicContentTypeManager(_mockDataContext.Object); - var contentTypeId = Constants.CONTENTTYPE_UpdateContentTypeId; - var contentType = new DynamicContentType - { - ContentTypeId = contentTypeId, - Name = Constants.CONTENTTYPE_UpdateContentType - }; + var contentType = GetValidUpdateContentType(); + _mockContentTypeRepository.Setup(r => r.Get(contentType.PortalId)).Returns(new[] { contentType }); - var fieldDefinitionCount = 5; + const int fieldDefinitionCount = 5; for (int i = 0; i < fieldDefinitionCount; i++) { contentType.FieldDefinitions.Add(new FieldDefinition()); @@ -622,8 +658,9 @@ public void UpdateContentType_Sets_ContentTypeId_Property_Of_New_New_FieldDefini //Assert foreach (var field in contentType.FieldDefinitions) { - Assert.AreEqual(contentTypeId, field.ContentTypeId); + Assert.AreEqual(contentType.ContentTypeId, field.ContentTypeId); } + _mockContentTypeRepository.VerifyAll(); } [Test] @@ -632,16 +669,13 @@ public void UpdateContentType_Updates_Existing_FieldDefinitions_On_Valid_Content //Arrange var contentTypeController = new DynamicContentTypeManager(_mockDataContext.Object); - var contentType = new DynamicContentType - { - ContentTypeId = Constants.CONTENTTYPE_UpdateContentTypeId, - Name = Constants.CONTENTTYPE_UpdateContentType - }; + var contentType = GetValidUpdateContentType(); + _mockContentTypeRepository.Setup(r => r.Get(contentType.PortalId)).Returns(new[] { contentType }); - var fieldDefinitionCount = 5; + const int fieldDefinitionCount = 5; for (int i = 0; i < fieldDefinitionCount; i++) { - contentType.FieldDefinitions.Add(new FieldDefinition() {FieldDefinitionId = Constants.CONTENTTYPE_ValidFieldDefinitionId}); + contentType.FieldDefinitions.Add(new FieldDefinition {FieldDefinitionId = Constants.CONTENTTYPE_ValidFieldDefinitionId}); } //Act @@ -649,6 +683,7 @@ public void UpdateContentType_Updates_Existing_FieldDefinitions_On_Valid_Content //Assert _mockFieldDefinitionController.Verify(fd => fd.UpdateFieldDefinition(It.IsAny()), Times.Exactly(fieldDefinitionCount)); + _mockContentTypeRepository.VerifyAll(); } [Test] @@ -657,11 +692,8 @@ public void UpdateContentType_Adds_New_Templates_On_Valid_ContentType() //Arrange var contentTypeController = new DynamicContentTypeManager(_mockDataContext.Object); - var contentType = new DynamicContentType - { - ContentTypeId = Constants.CONTENTTYPE_UpdateContentTypeId, - Name = Constants.CONTENTTYPE_UpdateContentType - }; + var contentType = GetValidUpdateContentType(); + _mockContentTypeRepository.Setup(r => r.Get(contentType.PortalId)).Returns(new[] { contentType }); var contentTemplateCount = 5; for (int i = 0; i < contentTemplateCount; i++) @@ -674,6 +706,7 @@ public void UpdateContentType_Adds_New_Templates_On_Valid_ContentType() //Assert _mockContentTemplateController.Verify(ct => ct.AddContentTemplate(It.IsAny()), Times.Exactly(contentTemplateCount)); + _mockContentTypeRepository.VerifyAll(); } [Test] @@ -682,14 +715,10 @@ public void UpdateContentType_Sets_ContentTypeId_Property_Of_New_New_Templates_O //Arrange var contentTypeController = new DynamicContentTypeManager(_mockDataContext.Object); - var contentTypeId = Constants.CONTENTTYPE_UpdateContentTypeId; - var contentType = new DynamicContentType - { - ContentTypeId = contentTypeId, - Name = Constants.CONTENTTYPE_UpdateContentType - }; + var contentType = GetValidUpdateContentType(); + _mockContentTypeRepository.Setup(r => r.Get(contentType.PortalId)).Returns(new[] { contentType }); - var contentTemplateCount = 5; + const int contentTemplateCount = 5; for (int i = 0; i < contentTemplateCount; i++) { contentType.Templates.Add(new ContentTemplate()); @@ -702,8 +731,9 @@ public void UpdateContentType_Sets_ContentTypeId_Property_Of_New_New_Templates_O //Assert foreach (var template in contentType.Templates) { - Assert.AreEqual(contentTypeId, template.ContentTypeId); + Assert.AreEqual(contentType.ContentTypeId, template.ContentTypeId); } + _mockContentTypeRepository.VerifyAll(); } [Test] @@ -712,16 +742,13 @@ public void UpdateContentType_Updates_Existing_Templates_On_Valid_ContentType() //Arrange var contentTypeController = new DynamicContentTypeManager(_mockDataContext.Object); - var contentType = new DynamicContentType - { - ContentTypeId = Constants.CONTENTTYPE_UpdateContentTypeId, - Name = Constants.CONTENTTYPE_UpdateContentType - }; + var contentType = GetValidUpdateContentType(); + _mockContentTypeRepository.Setup(r => r.Get(contentType.PortalId)).Returns(new[] { contentType }); - var contentTemplateCount = 5; + const int contentTemplateCount = 5; for (int i = 0; i < contentTemplateCount; i++) { - contentType.Templates.Add(new ContentTemplate() {TemplateId = Constants.CONTENTTYPE_ValidContentTemplateId}); + contentType.Templates.Add(new ContentTemplate {TemplateId = Constants.CONTENTTYPE_ValidContentTemplateId}); } @@ -730,30 +757,59 @@ public void UpdateContentType_Updates_Existing_Templates_On_Valid_ContentType() //Assert _mockContentTemplateController.Verify(ct => ct.UpdateContentTemplate(It.IsAny()), Times.Exactly(contentTemplateCount)); + _mockContentTypeRepository.VerifyAll(); } [Test] public void UpdateContentType_Updates_LastModifed_Proeprty_On_Valid_ContentType() { //Arrange - var userId = Constants.USER_ValidId; + const int userId = Constants.USER_ValidId; var contentTypeController = new DynamicContentTypeManager(_mockDataContext.Object); var mockUserController = new Mock(); mockUserController.Setup(uc => uc.GetCurrentUserInfo()).Returns(new UserInfo { UserID = userId }); UserController.SetTestableInstance(mockUserController.Object); - var contentType = new DynamicContentType - { - ContentTypeId = Constants.CONTENTTYPE_UpdateContentTypeId, - Name = Constants.CONTENTTYPE_UpdateContentType - }; + var contentType = GetValidUpdateContentType(); + _mockContentTypeRepository.Setup(r => r.Get(contentType.PortalId)).Returns(new[] { contentType }); //Act contentTypeController.UpdateContentType(contentType); //Assert Assert.AreEqual(userId, contentType.LastModifiedByUserId); + _mockContentTypeRepository.VerifyAll(); + } + #endregion + + private DynamicContentType GetValidContentType() + { + return new DynamicContentType + { + Name = Constants.CONTENTTYPE_ValidContentType, + PortalId = 0, + ContentTypeId = Constants.CONTENTTYPE_ValidContentTypeId + }; + } + + private DynamicContentType GetValidUpdateContentType() + { + return new DynamicContentType + { + Name = Constants.CONTENTTYPE_UpdateContentType, + PortalId = 0, + ContentTypeId = Constants.CONTENTTYPE_UpdateContentTypeId + }; + } + + private DynamicContentType GetValidNewContentType() + { + return new DynamicContentType + { + Name = Constants.CONTENTTYPE_ValidContentType, + PortalId = 0 + }; } private List CreateValidContentTypes(int count) diff --git a/Website/App_GlobalResources/Exceptions.resx b/Website/App_GlobalResources/Exceptions.resx index 58081b8e135..856f7937c78 100644 --- a/Website/App_GlobalResources/Exceptions.resx +++ b/Website/App_GlobalResources/Exceptions.resx @@ -474,7 +474,10 @@ Asp.net membership update user failed. Possible Reason(s): you enabled requiresUniqueEmail in membership config, but there have different user(s) used same email address with current user. - - Global Data Types can only be created, modified or deleted by Super Users + + System Data Types can only be created, modified or deleted by Super Users + + + System Content Types can only be created, modified or deleted by Super Users \ No newline at end of file