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.