Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

July 7 - July 17 | Round 2 of the Power BI Dataviz World Championships. Don't miss your chance! Learn more

Reply
Sandy_22
Frequent Visitor

Dynamic RLS combination

Hi everyone,
Need your help!

I am designing a report where users must be restricted across three dimensions: Study, Sub-Category, and Country.

My security permissions come from two separate database tables linked by a common study key:
user country table(Contains: User Email, study key, country key)
sub category table (Contains: User Email, study key, sub category key)

Initially, I applied RLS directly to my dimension tables and letting those filters flow down to my Fact tables as a star schema model.
However, because the security rules are split across two tables, and evaluates them independently. It ends up cross-joining the permitted values, creating a combination leak.

Example :

A user is explicitly allowed to see only Study 1 + Country A AND Study 2 + Country B.
Because the dimensions filter independently, the report mistakenly allows them to see Study 1 + Country B (an unauthorized combination).

 

After searching for so long, I got only 2 options:

1. To use bridge table between 2 security table and fact tables with bi directional filters. For 2 security tables, that would be 16 relationship with 8 bi-directional relationship between security table and bridge table.
2. Another option is to implement the RLS directly on fact table as it has all the combinations available.
Please suggest me with right option or any other options to implement this.

Note: we have split the security table because of large data.

Thanks in Advance!

1 ACCEPTED SOLUTION
oussamahaimoud
Memorable Member
Memorable Member

Hi @Sandy_22,

 

Go with Option 2. It's the only approach that correctly enforces combination-level security without model complexity or filter ambiguity.

 

Your Fact table naturally holds the exact valid combinations of Study + Country + Sub-Category. This is precisely what you want to enforce. You're not inferring permissions from two independent dimensions, you're directly validating each row against what the user is explicitly allowed to see.

The DAX for your RLS filter on the Fact table would look like this:

VAR __UserEmail = USERPRINCIPALNAME()

 

VAR __AllowedCountries =

    CALCULATETABLE(

        VALUES( UserCountryTable[StudyKey] ),

        UserCountryTable[UserEmail] = __UserEmail

    )

 

RETURN

    CONTAINS(

        CALCULATETABLE(

            SELECTCOLUMNS(

                UserCountryTable,

                "sk", UserCountryTable[StudyKey],

                "ck", UserCountryTable[CountryKey]

            ),

            UserCountryTable[UserEmail] = __UserEmail

        ),

        [StudyKey], FactTable[StudyKey],

        [CountryKey], FactTable[CountryKey]

    )

    &&

    CONTAINS(

        CALCULATETABLE(

            SELECTCOLUMNS(

                SubCategoryTable,

                "sk", SubCategoryTable[StudyKey],

                "sck", SubCategoryTable[SubCategoryKey]

            ),

            SubCategoryTable[UserEmail] = __UserEmail

        ),

        [StudyKey], FactTable[StudyKey],

        [SubCategoryKey], FactTable[SubCategoryKey]

    )

 

This enforces Study+Country as a pair from the first table, and Study+SubCategory as a pair from the second, exactly matching your permission model, with no cross-join leakage.

 

You mentioned the tables were split because of large data. The CONTAINS approach filters per-user at query time, so only the current user's rows from both security tables are evaluated, it doesn't scan the full tables. Performance remains acceptable in practice, especially if both security tables have UserEmail indexed or in a relationship that Power BI can leverage.

If performance becomes an issue at scale, the right optimization is a pre-joined security view at the database level (Study + Country + SubCategory per user, in a single flat table), which you then use as a single RLS source on the Fact table. This keeps the model clean without any bidirectional relationship complexity.

 

Assisted by AI for clarty of wording!


  Did my response help you? Clicking Kudos is a small gesture that goes a long way, it encourages contributors and helps the community thrive!


Did I answer your question? Please mark my post as a Solution, it helps others find the answer faster.


Senior Data & BI Consultant · Microsoft Fabric & Power BI Specialist


Connect with me on LinkedIn

View solution in original post

3 REPLIES 3
v-anbandari
Community Support
Community Support

Hi @Sandy_22,

Just checking in again on your issue. Could you please confirm if the solution worked?

Please let us know if you need any further assistance. Sharing an update can really help others facing the same thing.

Thank you.

v-anbandari
Community Support
Community Support

Hi @Sandy_22 ,

Thank you for posting your query in the Microsoft Fabric Community Forum, and thanks to @oussamahaimoud for sharing valuable insights.

 

Could you please confirm if your query has been resolved by the provided solutions? This would be helpful for other members who may encounter similar issues.

 

Thank you for being part of the Microsoft Fabric Community

oussamahaimoud
Memorable Member
Memorable Member

Hi @Sandy_22,

 

Go with Option 2. It's the only approach that correctly enforces combination-level security without model complexity or filter ambiguity.

 

Your Fact table naturally holds the exact valid combinations of Study + Country + Sub-Category. This is precisely what you want to enforce. You're not inferring permissions from two independent dimensions, you're directly validating each row against what the user is explicitly allowed to see.

The DAX for your RLS filter on the Fact table would look like this:

VAR __UserEmail = USERPRINCIPALNAME()

 

VAR __AllowedCountries =

    CALCULATETABLE(

        VALUES( UserCountryTable[StudyKey] ),

        UserCountryTable[UserEmail] = __UserEmail

    )

 

RETURN

    CONTAINS(

        CALCULATETABLE(

            SELECTCOLUMNS(

                UserCountryTable,

                "sk", UserCountryTable[StudyKey],

                "ck", UserCountryTable[CountryKey]

            ),

            UserCountryTable[UserEmail] = __UserEmail

        ),

        [StudyKey], FactTable[StudyKey],

        [CountryKey], FactTable[CountryKey]

    )

    &&

    CONTAINS(

        CALCULATETABLE(

            SELECTCOLUMNS(

                SubCategoryTable,

                "sk", SubCategoryTable[StudyKey],

                "sck", SubCategoryTable[SubCategoryKey]

            ),

            SubCategoryTable[UserEmail] = __UserEmail

        ),

        [StudyKey], FactTable[StudyKey],

        [SubCategoryKey], FactTable[SubCategoryKey]

    )

 

This enforces Study+Country as a pair from the first table, and Study+SubCategory as a pair from the second, exactly matching your permission model, with no cross-join leakage.

 

You mentioned the tables were split because of large data. The CONTAINS approach filters per-user at query time, so only the current user's rows from both security tables are evaluated, it doesn't scan the full tables. Performance remains acceptable in practice, especially if both security tables have UserEmail indexed or in a relationship that Power BI can leverage.

If performance becomes an issue at scale, the right optimization is a pre-joined security view at the database level (Study + Country + SubCategory per user, in a single flat table), which you then use as a single RLS source on the Fact table. This keeps the model clean without any bidirectional relationship complexity.

 

Assisted by AI for clarty of wording!


  Did my response help you? Clicking Kudos is a small gesture that goes a long way, it encourages contributors and helps the community thrive!


Did I answer your question? Please mark my post as a Solution, it helps others find the answer faster.


Senior Data & BI Consultant · Microsoft Fabric & Power BI Specialist


Connect with me on LinkedIn

Helpful resources

Announcements
FabCon and SQLCon Barcelona 2026

FabCon & SQLCon – Barcelona 2026

Join us in Barcelona for FabCon and SQLCon, the Fabric, Power BI, SQL, and AI community event. Save €200 with code FABCMTY200.

July Power BI Update Carousel

Power BI Monthly Update - July 2026

Check out the July 2026 Power BI update to learn about new features.

60 days of Data Days Carousel

Data Days 2026

Join Data Days 2026: 60 days of free live/on-demand sessions, challenges, study groups, and certification opportunities.

Power BI DataViz World Championships carousel

Power BI DataViz World Championships - June 2026

A new Power BI DataViz World Championship is coming this June! Don't miss out on submitting your entry.

Top Solution Authors