Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent creating new root CTD, and disable inheriting from ContentType. Uploading only 'ContentType' under the ContentTypes. #1909

Merged
merged 2 commits into from
May 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/ContentRepository/SR.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using SenseNet.ContentRepository.i18n;
// ReSharper disable InconsistentNaming

namespace SenseNet.ContentRepository
{
Expand All @@ -15,7 +16,9 @@ internal static class Registration
{
internal static string Msg_NodeTypeMustBeInheritedFromNode_1 = "NodeType must be inherited from Node: {0}";
internal static string Msg_DefinedHandlerIsNotAContentHandler = "Invalid ContentTypeDefinition: defined handler is not a ContentHandler";
internal static string Msg_MissingParentContentType = "Parent ContentType is not found (missing 'parentType' attribute).";
internal static string Msg_UnknownParentContentType = "Parent ContentType is not found";
internal static string Msg_ForbiddenParentContentType = "Parent ContentType cannot be 'ContentType'";
internal static string Msg_DataTypeCollisionInTwoProperties_4 = "DataType collision in two properties. NodeType = '{0}', PropertyType = '{1}', original DataType = {2}, passed DataType = {3}.";

// Attribute parsing
Expand Down
8 changes: 7 additions & 1 deletion src/ContentRepository/Schema/ContentTypeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -300,10 +300,16 @@ internal static ContentType LoadOrCreateNew(IXPathNavigable contentTypeDefinitio

// #3 Parent Node: if it is loaded yet use it (ReferenceEquals)
Node parentNode;
if (String.IsNullOrEmpty(parentTypeName))
if (string.IsNullOrEmpty(parentTypeName))
{
if(name != "ContentType" && name != "GenericContent")
throw new ContentRegistrationException(SR.Exceptions.Registration.Msg_MissingParentContentType, name);
parentNode = (Folder)Node.LoadNode(Repository.ContentTypesFolderPath);
}
else if (parentTypeName == "ContentType")
{
throw new ContentRegistrationException(SR.Exceptions.Registration.Msg_ForbiddenParentContentType, name);
}
else
{
parentNode = Instance.GetContentTypeByName(parentTypeName);
Expand Down
5 changes: 5 additions & 0 deletions src/Services.Core/Operations/UploadHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,11 @@ public async Task<object> ExecuteAsync(CancellationToken cancellationToken)
if (!AllowCreationForEmptyAllowedContentTypes(Content.ContentHandler))
throw new Exception(SenseNetResourceManager.Current.GetString("Action","UploadExceptionEmptyAllowedChildTypes"));

// Only ContentType is allowed under the System/Schema/ContentTypes
if (this.Content.Path.StartsWith(Repository.ContentTypesFolderPath + "/", StringComparison.InvariantCultureIgnoreCase))
if (this.ContentTypeName != "ContentType")
throw new Exception(SenseNetResourceManager.Current.GetString("Action", "UploadExceptionInvalidContentType"));

// the create parameter is sent in the url
if (Create.HasValue)
{
Expand Down
30 changes: 16 additions & 14 deletions src/Tests/SenseNet.ContentRepository.Tests/FieldSettingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public void FieldSetting_Structure()
var testRoot = CreateTestRoot();

string ctd = @"<?xml version='1.0' encoding='utf-8'?>
<ContentType name='FieldSetting_Structure' handler='SenseNet.ContentRepository.GenericContent' xmlns='http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition'>
<ContentType name='FieldSetting_Structure' parentType='GenericContent' handler='SenseNet.ContentRepository.GenericContent' xmlns='http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition'>
<Fields>
<Field name='TestString' type='ShortText'>
<Configuration>
Expand Down Expand Up @@ -311,7 +311,7 @@ public void FieldSetting_ReadOnlyAndCompulsoryOnGenericProperty()
bool compulsory;

string ctd = @"<?xml version='1.0' encoding='utf-8'?>
<ContentType name='FieldSetting_Structure' handler='SenseNet.ContentRepository.GenericContent' xmlns='http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition'>
<ContentType name='FieldSetting_Structure' parentType='GenericContent' handler='SenseNet.ContentRepository.GenericContent' xmlns='http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition'>
<Fields>
<Field name='TestString' type='ShortText'>
<Configuration>
Expand All @@ -327,7 +327,7 @@ public void FieldSetting_ReadOnlyAndCompulsoryOnGenericProperty()
Assert.IsFalse(compulsory, "#2");

ctd = @"<?xml version='1.0' encoding='utf-8'?>
<ContentType name='FieldSetting_Structure' handler='SenseNet.ContentRepository.GenericContent' xmlns='http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition'>
<ContentType name='FieldSetting_Structure' parentType='GenericContent' handler='SenseNet.ContentRepository.GenericContent' xmlns='http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition'>
<Fields>
<Field name='TestString' type='ShortText'>
<Configuration>
Expand All @@ -343,7 +343,7 @@ public void FieldSetting_ReadOnlyAndCompulsoryOnGenericProperty()
Assert.IsFalse(compulsory, "#4");

ctd = @"<?xml version='1.0' encoding='utf-8'?>
<ContentType name='FieldSetting_Structure' handler='SenseNet.ContentRepository.GenericContent' xmlns='http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition'>
<ContentType name='FieldSetting_Structure' parentType='GenericContent' handler='SenseNet.ContentRepository.GenericContent' xmlns='http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition'>
<Fields>
<Field name='TestString' type='ShortText'>
<Configuration>
Expand All @@ -359,7 +359,7 @@ public void FieldSetting_ReadOnlyAndCompulsoryOnGenericProperty()
Assert.IsTrue(compulsory, "#6");

ctd = @"<?xml version='1.0' encoding='utf-8'?>
<ContentType name='FieldSetting_Structure' handler='SenseNet.ContentRepository.GenericContent' xmlns='http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition'>
<ContentType name='FieldSetting_Structure' parentType='GenericContent' handler='SenseNet.ContentRepository.GenericContent' xmlns='http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition'>
<Fields>
<Field name='TestString' type='ShortText'>
<Configuration>
Expand All @@ -386,7 +386,7 @@ public void FieldSetting_ReadWriteOnReadOnlyProperty()
bool compulsory;

string ctd = @"<?xml version='1.0' encoding='utf-8'?>
<ContentType name='FieldSetting_Structure' handler='SenseNet.ContentRepository.GenericContent' xmlns='http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition'>
<ContentType name='FieldSetting_Structure' parentType='GenericContent' handler='SenseNet.ContentRepository.GenericContent' xmlns='http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition'>
<Fields>
<Field name='Id' type='Integer'>
<Configuration>
Expand All @@ -411,7 +411,7 @@ public void FieldSetting_ReadOnlyAndCompulsory_Reset()
bool compulsory;

string ctd = @"<?xml version='1.0' encoding='utf-8'?>
<ContentType name='FieldSetting_Structure' handler='SenseNet.ContentRepository.GenericContent' xmlns='http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition'>
<ContentType name='FieldSetting_Structure' parentType='GenericContent' handler='SenseNet.ContentRepository.GenericContent' xmlns='http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition'>
<Fields>
<Field name='TestString' type='ShortText'>
<Configuration>
Expand All @@ -427,7 +427,7 @@ public void FieldSetting_ReadOnlyAndCompulsory_Reset()
Assert.IsTrue(compulsory, "#2");

ctd = @"<?xml version='1.0' encoding='utf-8'?>
<ContentType name='FieldSetting_Structure' handler='SenseNet.ContentRepository.GenericContent' xmlns='http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition'>
<ContentType name='FieldSetting_Structure' parentType='GenericContent' handler='SenseNet.ContentRepository.GenericContent' xmlns='http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition'>
<Fields>
<Field name='TestString' type='ShortText' />
</Fields>
Expand Down Expand Up @@ -464,7 +464,7 @@ public void FieldSetting_Reference()
//====

ctd = @"<?xml version='1.0' encoding='utf-8'?>
<ContentType name='FieldSetting_Structure' handler='SenseNet.ContentRepository.GenericContent' xmlns='http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition'>
<ContentType name='FieldSetting_Structure' parentType='GenericContent' handler='SenseNet.ContentRepository.GenericContent' xmlns='http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition'>
<Fields>
<Field name='RefTest' type='Reference'>
<Configuration />
Expand All @@ -483,7 +483,7 @@ public void FieldSetting_Reference()
//====

ctd = @"<?xml version='1.0' encoding='utf-8'?>
<ContentType name='FieldSetting_Structure' handler='SenseNet.ContentRepository.GenericContent' xmlns='http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition'>
<ContentType name='FieldSetting_Structure' parentType='GenericContent' handler='SenseNet.ContentRepository.GenericContent' xmlns='http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition'>
<Fields>
<Field name='RefTest' type='Reference'>
<Configuration>
Expand All @@ -506,7 +506,7 @@ public void FieldSetting_Reference()
//====

ctd = @"<?xml version='1.0' encoding='utf-8'?>
<ContentType name='FieldSetting_Structure' handler='SenseNet.ContentRepository.GenericContent' xmlns='http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition'>
<ContentType name='FieldSetting_Structure' parentType='GenericContent' handler='SenseNet.ContentRepository.GenericContent' xmlns='http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition'>
<Fields>
<Field name='RefTest' type='Reference'>
<Configuration>
Expand Down Expand Up @@ -2020,7 +2020,7 @@ public void FieldSetting_ToXml_Binary()
private ContentType CreateContentType(string[] fieldXmlFragments)
{
var ctd = string.Format(@"<?xml version='1.0' encoding='utf-8'?>
<ContentType name='FieldSetting_Structure' handler='SenseNet.ContentRepository.GenericContent' xmlns='http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition'>
<ContentType name='FieldSetting_Structure' parentType='GenericContent' handler='SenseNet.ContentRepository.GenericContent' xmlns='http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition'>
<Fields>
{0}
</Fields>
Expand Down Expand Up @@ -2150,7 +2150,7 @@ public void FieldSetting_IndexingInfo_TryOverride()
try
{
string ctd = @"<?xml version='1.0' encoding='utf-8'?>
<ContentType name='FieldSetting_Structure' handler='SenseNet.ContentRepository.GenericContent' xmlns='http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition'>
<ContentType name='FieldSetting_Structure' parentType='GenericContent' handler='SenseNet.ContentRepository.GenericContent' xmlns='http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition'>
<Fields>
<Field name='Id' type='Integer'>
<Indexing>
Expand Down Expand Up @@ -2184,6 +2184,8 @@ private string InstallContentType(string contentTypeDefInstall, string contentTy
{
SchemaEditor ed1 = new SchemaEditor();
SchemaEditor ed2 = new SchemaEditor();
ed1.Load();
ed2.Load();

ContentTypeManagerAccessor ctmAcc = new ContentTypeManagerAccessor(ContentTypeManager.Current);
ContentType cts = ctmAcc.LoadOrCreateNew(contentTypeDefInstall);
Expand All @@ -2197,7 +2199,7 @@ private string InstallContentType(string contentTypeDefInstall, string contentTy

if (contentTypeDefModify != null)
{
//-- Id-k beallitasa es klonozas
//-- Set ids and make clones
SchemaEditor ed3 = new SchemaEditor();
SchemaEditorAccessor ed3Acc = new SchemaEditorAccessor(ed3);
SchemaItemAccessor schItemAcc;
Expand Down
149 changes: 148 additions & 1 deletion src/Tests/SenseNet.ContentRepository.Tests/UploadHandlerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Primitives;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SenseNet.ContentRepository.Schema;
using SenseNet.Services.Core.Operations;
using SenseNet.Tests.Core;

Expand Down Expand Up @@ -113,7 +114,6 @@ public void UploadHandler_Update_TextToText()
});
}


private object UploadTextFile(string text, Content parent, string fileName, bool overwrite)
{
var httpContext = CreateHttpContext("/Root/Content/DocLib");
Expand All @@ -140,6 +140,153 @@ private object UploadTextFile(string text, Content parent, string fileName, bool
var response = handler.ExecuteAsync(CancellationToken.None).GetAwaiter().GetResult();
return response;
}

/* ========================================================================= Upload CTD */

private readonly string _ctd = @"<?xml version=""1.0"" encoding=""utf-8""?>
<ContentType name=""TestContent"" parentType=""Folder"" handler=""SenseNet.ContentRepository.Folder"" xmlns=""http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition"">
<Fields><Field name=""AppInfo"" type=""ShortText""><DisplayName>AppInfo</DisplayName></Field></Fields>
</ContentType>";

[TestMethod]
public void UploadHandler_Create_ContentType()
{
Test(() =>
{
var parent = Content.Load("/Root/System/Schema/ContentTypes/GenericContent");

// ACT
UploadCtd(_ctd, parent, "ContentType", "TestContent");

// ASSERT
Assert.IsNotNull(ContentType.GetByName("TestContent"));

var createdContent = Content.Load("/Root/System/Schema/ContentTypes/GenericContent/Folder/TestContent");
Assert.IsNotNull(createdContent);
Assert.AreEqual("ContentType", createdContent.ContentType.Name);

var createdText =
RepositoryTools.GetStreamString(createdContent.ContentHandler.GetBinary("Binary").GetStream());
Assert.AreEqual(_ctd, createdText);
});
}
[TestMethod]
public void UploadHandler_Create_ContentType_AsFile()
{
Test(() =>
{
var parent = Content.Load("/Root/System/Schema/ContentTypes/GenericContent");

// ACT
try
{
UploadCtd(_ctd, parent, "File", "TestContent");
Assert.Fail("The expected exception was not thrown.");
}
catch (Exception ex)
{
// do nothing
}

// ASSERT
Assert.IsNull(ContentType.GetByName("TestContent"));

var createdContent = Content.Load("/Root/System/Schema/ContentTypes/GenericContent/Folder/TestContent");
Assert.IsNull(createdContent);
createdContent = Content.Load("/Root/System/Schema/ContentTypes/GenericContent/TestContent");
Assert.IsNull(createdContent);
createdContent = Content.Load("/Root/System/Schema/ContentTypes/TestContent");
Assert.IsNull(createdContent);
});
}

private readonly string _rootCtd = @"<?xml version=""1.0"" encoding=""utf-8""?>
<ContentType name=""TestContent"" handler=""SenseNet.ContentRepository.GenericContent"" xmlns=""http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition"">
<Fields><Field name=""AppInfo"" type=""ShortText""><DisplayName>AppInfo</DisplayName></Field></Fields>
</ContentType>";

[TestMethod]
public void UploadHandler_Create_RootContentType()
{
Test(() =>
{
var parent = Content.Load("/Root/System/Schema/ContentTypes/GenericContent");

// ACT
try
{
UploadCtd(_rootCtd, parent, "ContentType", "TestContent");
Assert.Fail("The expected exception was not thrown.");
}
catch (Exception ex)
{
// do nothing
}

// ASSERT
Assert.IsNull(ContentType.GetByName("TestContent"));

var createdContent = Content.Load("/Root/System/Schema/ContentTypes/GenericContent/TestContent");
Assert.IsNull(createdContent);
createdContent = Content.Load("/Root/System/Schema/ContentTypes/TestContent");
Assert.IsNull(createdContent);

});
}


private readonly string _wrongParentCtd = @"<?xml version=""1.0"" encoding=""utf-8""?>
<ContentType name=""TestContent"" parentType=""ContentType"" handler=""SenseNet.ContentRepository.GenericContent"" xmlns=""http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition"">
<Fields><Field name=""AppInfo"" type=""ShortText""><DisplayName>AppInfo</DisplayName></Field></Fields>
</ContentType>";

[TestMethod]
public void UploadHandler_Create_InheritFromContentType()
{
Test(() =>
{
var parent = Content.Load("/Root/System/Schema/ContentTypes/GenericContent");

// ACT
try
{
UploadCtd(_wrongParentCtd, parent, "ContentType", "TestContent");
Assert.Fail("The expected exception was not thrown.");
}
catch (Exception ex)
{
// do nothing
}

// ASSERT
Assert.IsNull(ContentType.GetByName("TestContent"));

var createdContent = Content.Load("/Root/System/Schema/ContentTypes/GenericContent/TestContent");
Assert.IsNull(createdContent);
createdContent = Content.Load("/Root/System/Schema/ContentTypes/TestContent");
Assert.IsNull(createdContent);

});
}

private object UploadCtd(string text, Content parent, string contentType, string fileName)
{
var httpContext = CreateHttpContext("/Root/Content/DocLib");

var handler = new UploadHandler(parent, httpContext)
{
FileName = fileName,
ContentTypeName = contentType,
PropertyName = "Binary",
Overwrite = true,
ChunkToken = "0*0*False*False",
FileText = text,
};

var response = handler.ExecuteAsync(CancellationToken.None).GetAwaiter().GetResult();
return response;
}

private HttpContext CreateHttpContext(string resource, string queryString = null, IServiceProvider services = null)
{
var httpContext = new DefaultHttpContext();
Expand Down