diff --git a/Jurassic.4.0.0.nupkg b/Jurassic.4.0.0.nupkg new file mode 100644 index 000000000..d9434a9de Binary files /dev/null and b/Jurassic.4.0.0.nupkg differ diff --git a/nuget.config b/nuget.config index b1738fc41..da7c4d0ca 100644 --- a/nuget.config +++ b/nuget.config @@ -2,5 +2,6 @@ + \ No newline at end of file diff --git a/nuget.exe b/nuget.exe new file mode 100644 index 000000000..ec1309c7a Binary files /dev/null and b/nuget.exe differ diff --git a/packages/jurassic/4.0.0/jurassic.4.0.0.nupkg b/packages/jurassic/4.0.0/jurassic.4.0.0.nupkg new file mode 100644 index 000000000..d9434a9de Binary files /dev/null and b/packages/jurassic/4.0.0/jurassic.4.0.0.nupkg differ diff --git a/packages/jurassic/4.0.0/jurassic.4.0.0.nupkg.sha512 b/packages/jurassic/4.0.0/jurassic.4.0.0.nupkg.sha512 new file mode 100644 index 000000000..369254cf8 --- /dev/null +++ b/packages/jurassic/4.0.0/jurassic.4.0.0.nupkg.sha512 @@ -0,0 +1 @@ +p41pr8YRXXiqrW5DrLpCxbsyTZwZ9ONcdFjYaHorvCuvPeMiBgl+WOFKkzLENEnJdh22tkh7FKjfSMW1S1jtZg== \ No newline at end of file diff --git a/packages/jurassic/4.0.0/jurassic.nuspec b/packages/jurassic/4.0.0/jurassic.nuspec new file mode 100644 index 000000000..feb71c615 --- /dev/null +++ b/packages/jurassic/4.0.0/jurassic.nuspec @@ -0,0 +1,16 @@ + + + + Jurassic + 4.0.0 + Paul Bartrum + Paul Bartrum + false + A .NET library to parse and execute JavaScript code. + + + + + + + \ No newline at end of file diff --git a/src/Squidex.Domain.Apps.Core/Scripting/IScriptEngine.cs b/src/Squidex.Domain.Apps.Core/Scripting/IScriptEngine.cs new file mode 100644 index 000000000..f137d66e9 --- /dev/null +++ b/src/Squidex.Domain.Apps.Core/Scripting/IScriptEngine.cs @@ -0,0 +1,20 @@ +// ========================================================================== +// IScriptEngine.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System.Threading.Tasks; +using Squidex.Domain.Apps.Core.Contents; + +namespace Squidex.Domain.Apps.Core.Scripting +{ + public interface IScriptEngine + { + Task ExecuteAsync(ScriptContext context, string operationName, string script); + + Task ExecuteAndTransformAsync(ScriptContext context, string operationName, string script); + } +} diff --git a/src/Squidex.Domain.Apps.Core/Scripting/JurassicScriptEngine.cs b/src/Squidex.Domain.Apps.Core/Scripting/JurassicScriptEngine.cs new file mode 100644 index 000000000..105614039 --- /dev/null +++ b/src/Squidex.Domain.Apps.Core/Scripting/JurassicScriptEngine.cs @@ -0,0 +1,103 @@ +// ========================================================================== +// JurassicScriptEngine.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; +using System.Security; +using System.Threading.Tasks; +using Jurassic; +using Jurassic.Library; +using Newtonsoft.Json; +using Squidex.Domain.Apps.Core.Contents; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Tasks; + +// ReSharper disable ConvertToLambdaExpression + +namespace Squidex.Domain.Apps.Core.Scripting +{ + public sealed class JurassicScriptEngine : IScriptEngine + { + private readonly JsonSerializerSettings serializerSettings; + + public JurassicScriptEngine(JsonSerializerSettings serializerSettings) + { + Guard.NotNull(serializerSettings, nameof(serializerSettings)); + + this.serializerSettings = serializerSettings; + } + + public Task ExecuteAsync(ScriptContext context, string operationName, string script) + { + Guard.NotNull(context, nameof(context)); + + if (!string.IsNullOrWhiteSpace(script)) + { + return TaskHelper.False; + } + + var engine = CreateScriptEngine(context, operationName); + + engine.Execute(script); + + return TaskHelper.False; + } + + public Task ExecuteAndTransformAsync(ScriptContext context, string operationName, string script) + { + Guard.NotNull(context, nameof(context)); + + if (!string.IsNullOrWhiteSpace(script)) + { + return Task.FromResult(context.Data); + } + + var result = context.Data; + + var engine = CreateScriptEngine(context, operationName); + + engine.SetGlobalFunction("replace", new Action(data => + { + try + { + result = JsonConvert.DeserializeObject(JSONObject.Stringify(engine, data)); + } + catch + { + result = new NamedContentData(); + } + })); + + engine.Execute(script); + + return Task.FromResult(result); + } + + private ScriptEngine CreateScriptEngine(ScriptContext context, string operationName) + { + Guard.NotNullOrEmpty(operationName, nameof(operationName)); + + var engine = new ScriptEngine(); + + engine.SetGlobalFunction("disallow", new Action(message => + { + throw new SecurityException(message); + })); + + engine.SetGlobalFunction("reject", new Action(message => + { + throw new ValidationException($"Failed to '{operationName}", !string.IsNullOrWhiteSpace(message) ? new[] { new ValidationError(message) } : null); + })); + + var json = JsonConvert.SerializeObject(context, serializerSettings); + + engine.SetGlobalValue("ctx", JSONObject.Parse(engine, json)); + + return engine; + } + } +} diff --git a/src/Squidex.Domain.Apps.Core/Scripting/ScriptContext.cs b/src/Squidex.Domain.Apps.Core/Scripting/ScriptContext.cs new file mode 100644 index 000000000..fb21649a4 --- /dev/null +++ b/src/Squidex.Domain.Apps.Core/Scripting/ScriptContext.cs @@ -0,0 +1,24 @@ +// ========================================================================== +// ScriptContext.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; +using Squidex.Domain.Apps.Core.Contents; + +namespace Squidex.Domain.Apps.Core.Scripting +{ + public sealed class ScriptContext + { + public ScriptUser User { get; set; } + + public Guid ContentId { get; set; } + + public NamedContentData Data { get; set; } + + public NamedContentData OldData { get; set; } + } +} diff --git a/src/Squidex.Domain.Apps.Core/Scripting/ScriptUser.cs b/src/Squidex.Domain.Apps.Core/Scripting/ScriptUser.cs new file mode 100644 index 000000000..d0a25d3f3 --- /dev/null +++ b/src/Squidex.Domain.Apps.Core/Scripting/ScriptUser.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.Security.Claims; + +namespace Squidex.Domain.Apps.Core.Scripting +{ + public sealed class ScriptUser + { + public string Id { get; set; } + + public string Email { get; set; } + + public bool IsClient { get; set; } + + public Dictionary Scopes { get; } = new Dictionary(); + + public static ScriptUser Create(ClaimsPrincipal principal) + { + return new ScriptUser(); + } + } +} \ No newline at end of file diff --git a/src/Squidex.Domain.Apps.Core/Squidex.Domain.Apps.Core.csproj b/src/Squidex.Domain.Apps.Core/Squidex.Domain.Apps.Core.csproj index dff5093fa..c811f6da4 100644 --- a/src/Squidex.Domain.Apps.Core/Squidex.Domain.Apps.Core.csproj +++ b/src/Squidex.Domain.Apps.Core/Squidex.Domain.Apps.Core.csproj @@ -10,7 +10,9 @@ + +