Loading...

XDS policies in F&O: row-level security that actually fires

Extensible Data Security policies are the F&O answer to row-level access control. The docs show the mechanics. They don't show the traps - context clauses that silently return all rows, constrained tables that skip certain forms, and the performance cliff.

XDS policies in F&O: row-level security that actually fires

Security roles get users to tables. Row-level filtering inside those tables is what turns "all customers" into "customers in my country" or "transactions for my legal entity". Organizations with geographic salesforces, multi-legal-entity consolidations, or segregation-of-duties requirements run into this the moment they try to make Customer-360 work.

Extensible Data Security (XDS) is the F&O mechanism for this. It has four moving parts, and when any one is wrong the policy silently returns all rows - the worst failure mode because nobody notices until an audit.

What an XDS policy actually does

An XDS policy attaches a filter to a constrained table based on a primary table and a context clause. When a user in a specific context queries the constrained table, F&O appends an implicit WHERE clause derived from the primary table lookup.

The four pieces:

  1. Primary table + view - the source of the user-specific context (e.g., SalesEmployee linked to the current user)
  2. Constrained table + view - the target that needs filtering (e.g., CustTable)
  3. Policy query - the join between primary and constrained, with any additional filters
  4. Context clause - when the policy applies (role context, application context, or always)

When all four align, filtering works. When any is misconfigured, the policy quietly does nothing.

Three configuration traps

Context clause too broad

Setting Context Type to ContextString with a string no role actually sets means the policy exists but never activates. The most common outcome: a policy that looks right on paper but never appends its WHERE clause.

Fix: use RoleName context bound to the specific security role the filter should apply to. Don't use arbitrary context strings unless you're wiring them from code.

Policy query missing DataAreaId join

A policy joining SalesEmployee to CustTable often works in the user's default company and silently fails in other legal entities. The cross-company context doesn't add dataAreaId implicitly - the policy query needs it explicitly.

Every XDS policy query touching company-specific tables needs an explicit dataAreaId match, even when both tables are SaveDataPerCompany.

Constrained tables missing access paths

A policy constrains CustTable but users still see customer data via CustTransOpen on a collection report - the report queries CustTransOpen directly without going through CustTable, bypassing the filter.

Constrained tables must include every table in the access path you want to block. Building a coverage matrix from actual data-access patterns (forms, reports, queries) catches the ones developers miss.

Debugging a policy that doesn't fire

Work from the bottom of the stack up:

  1. Inspect query SQL via SQL Server Management Studio or Trace Parser. Run the form as the test user, capture the SQL, look for the expected WHERE clause. If missing, the policy isn't being appended.
  2. Check policy enabled via Security and Compliance Studio or SecurityPolicyTable. Policies can be disabled at runtime by configuration.
  3. Check role context - what role does the test user hold? Does the policy reference that exact role name?
  4. Check model deployment - policies live in a model; the model must be deployed to the environment for the policy to exist there.

Performance cost

XDS adds an implicit JOIN to every query against the constrained table. The query optimizer usually handles this well. Complex policies with multi-hop joins can degrade measurably, especially on large constrained tables.

Patterns that keep XDS fast:

  • Index the filter columns in both primary and constrained tables. The implicit JOIN needs support.
  • Minimize the primary view scope - return only the columns the policy uses.
  • Test with representative data - a policy fine on 100 rows can fall over at 500k.
  • Profile via Trace Parser when a common form slows after policy enablement.

Primary table relationships

The relationship between primary and constrained tables drives the query plan. Keep it simple: one primary table, direct join to the constrained table, explicit dataAreaId, explicit user binding. Multi-hop primary chains produce query plans the optimizer guesses at, and guesses are what go wrong at scale.

A workable multi-country pattern

Organizations running salespeople in multiple countries on a shared F&O tenant typically ship:

  • Primary table: SalesEmployee joined to UserRelations for the current-user binding
  • Constrained tables: CustTable, CustInvoiceTable, CustTrans, SalesTable, SalesLine (and every report-specific table that touches customer data)
  • Context: RoleName bound to a country-specific sales role
  • Query: explicit dataAreaId match, country filter from the primary view
  • Perf: indexes on SalesEmployee.UserID and all constrained-table primary keys

The work is one-off. The audit compliance it produces lasts years.

When the policy is working

A correctly configured XDS is invisible. Users don't see rows they shouldn't. Reports filter consistently across every entry point. The audit log shows filtered queries, not full-table scans. Nothing visually breaks. That invisibility is why the configuration discipline matters - when something is wrong, nobody notices.

Contact Us Now

Share Your Story

We build trust by delivering what we promise – the first time and every time!

We'd love to hear your vision. Our IT experts will reach out to you during business hours to discuss making it happen.

WHY CHOOSE US

"Collaborate, Elevate, Celebrate where Associates - Create Project Excellence"

SapotaCorp beyond the IT industry standard, we are

  • Certificated
  • Assured quality
  • Extra maintenance

Tell us about your project

close