Ticket T1120269
Visible to All Users
Duplicate

Urgent: ValueManagerContext.Storage is null in 22.1.5

created 2 years ago (modified 2 years ago)

Hello,

I realize there has been a change in XafApplication initialization from 22.1.3 to 22.1.5 but it's caused my application to break. I've created a sample application which illustrates the problem.

To reproduce the error, run the solution and log in as "User" (case sensitive as it's an InMemoryDataStore).
Click on the "Attachment.png" file in the list and the file is unable to download. If you convert the project back to 22.1.3, it works as expected.

This is caused by our ActiveOrgHelper class which relies on the IValueManager system to store an active guid value. We then use this value as part of our criteria:

defaultRole.AddObjectPermission<Container>(SecurityOperations.FullObjectAccess, "[CreatedBy.Oid] = CurrentUserId() OR [Organization.Oid] = **CurrentOrgId()**", SecurityPermissionState.Allow);

Since the change, we can no longer use this criteria in any non-XAF application initialized middleware such as your IFileUrlService. This breaks the structure of our application and now I'm looking for a solution.

Any help is appreciated.

Thanks
-Dave

Comments (1)
Dmitry M (DevExpress) 2 years ago

    Hello Dave,

    ValueManager has never been designed for XAF Blazor UI, Web API Service, and other non-XAF UI apps. It might have worked in Blazor UI by accident, but in fact, we have internal tests for ValueManager API only in WinForms and WebForms.

    We will emphasize this at https://docs.devexpress.com/eXpressAppFramework/DevExpress.Persistent.Base.ValueManager and probably remove this help topic in future versions so that XAF users will not accidentally rely on this internal API.

    ValueManager did not change in v22.1.5, but XAF's Blazor FileService did. The FileService class no longer relies on a BlazorApplication instance to upload data (BlazorApplication is in charge for initializing the ValueManager context). We intentionally made this change for performance reasons to avoid creating and configuring BlazorApplication every time one uploads a file or media data.

    In your application, you can notice that the FileAttachmentTestModule.Setup method is not called when you try to upload a file. XAF v22.2 will bring yet more changes like this one. Our ultimate goal is to use more and more REST API services inside these modern apps.

    As a temporary workaround (to return the old FileService behavior), you can do the following in XAF Blazor UI v22.1:

    Code
    public class MyObjectSpaceFactoryWrapper : DevExpress.ExpressApp.AspNetCore.Core.Internal.IObjectSpaceFactoryWrapper { private readonly IXafApplicationProvider xafApplicationProvider; public MyObjectSpaceFactoryWrapper(IXafApplicationProvider xafApplicationProvider) { this.xafApplicationProvider = xafApplicationProvider; } public IObjectSpace CreateObjectSpace(Type objectType) { return xafApplicationProvider.GetApplication().CreateObjectSpace(objectType); } // Register this service at the end of the Startup.ConfigureServices method: services.AddScoped<DevExpress.ExpressApp.AspNetCore.Core.Internal.IObjectSpaceFactoryWrapper, MyObjectSpaceFactoryWrapper>(); }

    NOTE: this workaround will not work in other usage scenarios, e.g. in Web API Service. Thus, we strongly recommend that you remove this code once we have a better solution available in v22.2.

    When talking about the XAF Blazor UI, Web API Service, and other non-XAF UI apps, our general recommendation is to avoid using SecuritySystem.CurrentUser, ValueManager, ActiveOrgHelper.Instance, and other static properties in the following cases:

    1. application.LoggedOn += (s, args) => { ActiveOrgHelper.Instance.ActiveOrganizationOid = ((ApplicationUser)application.Security.User).Organization.Oid; };
    2. Session.GetObjectByKey<OrganizationDetails>(ActiveOrgHelper.Instance.ActiveOrganizationOid);

    P.S.
    In your application, you can also use the ApplicationUser class instead of PermissionPolicyUser for the CreatedBy properties and API calls (unless you intentionally want to use the base class here).

    Answers approved by DevExpress Support

    created 2 years ago

    Hello, Dave.

    In v22.2, we introduced the following SecurityOptions.Events API:

    • OnCustomizeSecurityCriteriaOperator
    • OnCreateCustomSecurityFunctionPatcher

    The OnCustomizeSecurityCriteriaOperatorContext object has the following members:

    C#
    public ISecurityStrategyBase Security { get; } public CriteriaOperator Operator { get; } public CriteriaOperator? Result { get; set; } public IServiceProvider? ServiceProvider { get; }

    The CreateCustomSecurityFunctionPatcherContext object has the following members:

    C#
    public ISecurityStrategyBase Security { get; } public SecurityFunctionPatcher? CustomSecurityFunctionPatcher { get; set; } public IServiceProvider? ServiceProvider { get; }

    The OnCustomizeSecurityCriteriaOperator event allows you to customize the criteria as follows:

    Code
    options.Events.OnCustomizeSecurityCriteriaOperator = context => { if(context.Operator is FunctionOperator functionOperator) { if(functionOperator.Operands.Count == 1 && "CurrentOrgId".Equals((functionOperator.Operands[0] as ConstantValue)?.Value?.ToString(), StringComparison.InvariantCultureIgnoreCase)) { context.Result = new ConstantValue(((ApplicationUser)context.Security.User)?.Organization?.Oid ?? Guid.Empty); } } };

    The CreateCustomSecurityFunctionPatcher event helps you create a custom SecurityFunctionPatcher descendant:

    Code
    options.Events.OnCreateCustomSecurityFunctionPatcher = context => { context.CustomSecurityFunctionPatcher = new CustomSecurityFunctionPatcher(context.Security); };

    NOTE: with v22.2 you no longer need to implement ICustomFunctionOperator or ICustomFunctionOperatorConvertibleToExpression at all in EF Core 6+, because you return everything inside the OnCustomizeSecurityCriteriaOperator delegate. For XPO, you still need to implement and register a custom criteria function, as shown below. In the examples above, our custom criteria function was declared as follows:

    Code
    public class CurrentOrganizationCriteriaFunction : ICustomFunctionOperator, ICustomFunctionOperatorConvertibleToExpression { public const string OperatorName = "CurrentOrgId"; private static readonly Guid mockKey = Guid.NewGuid(); public static void Register() => CriteriaOperator.RegisterCustomFunction(new CurrentOrganizationCriteriaFunction()); public string Name => OperatorName; public object Evaluate(params object[] operands) => mockKey; public Type ResultType(params Type[] operands) => typeof(Guid); public Expression Convert(ICriteriaToExpressionConverter converter, params Expression[] operands) => Expression.Constant(mockKey); }

    Thanks,
    Dennis

    P.S.
    Can we make this ticket public?

      Comments (2)
      D D
      Dave Hesketh (Llamachant Technology) / DX MVP 2 years ago

        I have removed the original attachment and made this ticket public.

        Thanks!

        Dennis Garavsky (DevExpress) 2 years ago

          Appreciate it, I also duplicated this information in our Breaking Change.

          Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

          Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.