Sunday, November 10, 2019

D365/AX7 – How to Implement Security Policy – Context Type Context String

For a quick introduction to the security policies, please check the blog on How to Implement Security Policy – Context Type Role Name.

Problem

For some security requirements, the security policy using Context type – Role property can be used if a context is used to determine whether the policy should be applied. Whenever it is required this context string needs to be set by the application using the XDS::setXDSContext.

Solution

Let’s have a quick demonstration on how security policy can be implemented using ContextString. Consider you have to restrict project manager to view the purchase requisitions of the project which is managed by him. But making sure that user is not granted the access on all purchase requisitions at the same time and policy is applied while opening the project purchase requisitions form.

Step#1
Create a temporary table to store the record Ids of the table records which need to be restricted for the user.

Override the Xds method on table to insert the records in temporary table which should be allowed for user to view. Here we determine the viewable record set based on the permission provided to the user on the specified menu items for the same form.


public RefreshFrequency xds()
    {
        Unchecked(Uncheck::XDS)
        {
            Set resultSet = new Set(Types::Int64);

            if (HcmSecurity::allowAllRowsByMenuItem(AccessRight::View, SecurableType::MenuItemDisplay, menuItemDisplayStr(MY_ProjManagerPurchReqTable)))
            {
                this.insertFromPurchReqQueries(queryStr(MY_ProjManagerPurchReqPolicy), resultSet);
            }
            else if (HcmSecurity::allowAllRowsByMenuItem(AccessRight::View, SecurableType::MenuItemDisplay, menuItemDisplayStr(ProjPurchReqTable)))
            {
                this.insertFromPurchReqQueries(queryStr(ProjPurchReqCue), resultSet);
            }

            var enum = resultSet.getEnumerator();

            while (enum.moveNext())
            {
                MY_PurchReqTableXdsTmp purchReqTableXdsTmp;
                purchReqTableXdsTmp.PurchReqRefRecId = enum.current();
                purchReqTableXdsTmp.insert();
            }
        }

        return RefreshFrequency::PerSession;
    }

   
private void insertFromPurchReqQueries(str QueryName, Set _resultSet)
    {
        Query query = new Query(QueryName);
        QueryRun queryRun = new QueryRun(query);

        while (queryRun.next())
        {
            PurchReqTable purchReq = queryRun.get(tableNum(PurchReqTable));
            _resultSet.add(purchReq.RecId);
        }

    }

Step#2
Create an AOT query to join the table to restrict the records and the temporary table which holds the selective records to provide relevant access to the user as per security policy.



Step#3 Create a security policy and set the properties as below to apply the policy using the context string in application.



Context Type: ContextString
Context String: Any string value which can be used to configure the security policy using security configurations. In this case it is set to MY_PolicyForPurchReqHcmWorkers
Primary table: Select the table from the query as primary table to apply the record level security
Constraint Table: Set to ‘Yes’ if the Primary table should be used to restrict the records.
Operation: Select the operation which should be restricted on primary table using this security policy.
Query: Set the query you created in step 2.


Step#4 Now let’s apply this security based on the required conditions mentioned above. Create a class to handle operations related to required security policy.


public final class MY_PurchReqTableHcmWorkersPolicyHandler
{
    private const str PurchReqHcmWorkerPolicyContextString = 'MY_PolicyForPurchReqHcmWorkers';


    public static void flushPurchReqTableXdsTmp()
    {
        XDSServices services = new XDSServices();
        services.flushXDSMyConstructs(0, tableStr(MY_PurchReqTableXdsTmp));
    }

   
public static str getCurrentXdsContextString(XDSServices _xdsServices)
    {
        return _xdsServices.getXDSContext(0);
    }

   
public static void setCurrentXdsContextString(XDSServices _xdsServices, str _contextString = PurchReqHcmWorkerPolicyContextString)
    {
        _xdsServices.setXDSContext(0, _contextString);       
    }

   
public static void resetCurrentXdsContextString(XDSServices _xdsServices)
    {
        str prevXDSContext = _xdsServices.getXDSContext(0);
        _xdsServices.setXDSContext(0, '');

        if(prevXDSContext != PurchReqHcmWorkerPolicyContextString)
        {
            _xdsServices.setXDSContext(0, prevXDSContext);
        }
    }

    public static boolean enablePurchReqWorkerPolicy(Args _args, boolean _callerCheck = false)
    {
        NoYesId ret = false;

        switch(_args.menuItemName())
        {
            case menuItemDisplayStr(ProjPurchReqTable):
                ret = NoYes::Yes;
                break;

            default:
                if(!_callerCheck)
                {
                    FormRun callerFormRun = _args.caller();
                    if(callerFormRun)
                    {
                        ret = MY_PurchReqTableHcmWorkersPolicyHandler::enablePurchReqWorkerPolicy(callerFormRun.args(), true);
                    }
                }
        }

       
        return ret;
    }

    public static boolean enablePurchReqWorkerPolicyForProjManager(Args _args)
    {
        NoYesId ret = false;

        ret = MY_PurchReqTableHcmWorkersPolicyHandler::enablePurchReqWorkerPolicy(_args);
       
        ret = ret && !HcmSecurity::allowAllRowsByMenuItem(AccessRight::View, SecurableType::MenuItemDisplay, menuItemDisplayStr(PurchReqTableAll));

        return ret;
    }

}


Step#5 Create an event handler class for purchase requisition list page. Implement the event handlers for data source initialized event to reset the context string clearing the security policy effect, and query executed event to apply the context string.

class MY_PurchReqTableListPageSecurityPREventHandler
{
   
 [FormDataSourceEventHandler(formDataSourceStr(PurchReqTableListPage, PurchReqTable), FormDataSourceEventType::Initialized)]
    public static void PurchReqTable_OnInitialized(FormDataSource sender, FormDataSourceEventArgs e)
    {
        MY_PurchReqTableHcmWorkersPolicyHandler::resetCurrentXdsContextString(new XDSServices());
    }

    [FormDataSourceEventHandler(formDataSourceStr(PurchReqTableListPage, PurchReqTable), FormDataSourceEventType::QueryExecuting)]
    public static void PurchReqTable_OnQueryExecuting(FormDataSource sender, FormDataSourceEventArgs e)
    {
        FormRun     formRun = sender.formRun();
        XDSServices services;
        if(MY_PurchReqTableHcmWorkersPolicyHandler::enablePurchReqWorkerPolicyForProjManager(formRun.args()))
        {
            services = new XDSServices();           MY_PurchReqTableHcmWorkersPolicyHandler::setCurrentXdsContextString(services);
        }

    }

}


Build your model/project, synchronize database and the security policy is ready to be applied with the role.

Execution

If the user is not granted the permission to view “All purchase requisitions” and assigned the permission to access menu item MY_ProjManagerPurchReqTable, the user can view only those PRs which are related to the projects managed by him.

To check how to implement security policy using other context types, please check the blog.

1 comment:

  1. Mlife Casino | New Orleans, LA | JM Hub
    Experience the thrills 원주 출장안마 of gaming at Mlife 전주 출장마사지 casino. Enjoy slots, table games, blackjack, and 포천 출장안마 live 통영 출장안마 dealer casino games 성남 출장안마 in your neighborhood.

    ReplyDelete