Context
There are two typical solutions for dynamic RLS: 1) based on
user profile, which indicates what user can access what item(s); 2) based on
hierarchy, such as organizational hierarchy. However, this document will
introduce a practical requirement that doesn't follow the solutions mentioned
above. So instead, this new solution is intended to resolve a problem based on
both profile and hierarchy.
Requirement
For the sake of explanation, we use a
simple sample.
1)
We
have a hierarchy as below
Level1 |
Level2 |
Level3 |
Item11 |
Item21 |
Item31 |
Item32 |
||
Item22 |
Item33 |
|
Item34 |
2)
Each
item has a fact below
Item |
Measure |
Item11 |
100 |
Item21 |
50 |
Item22 |
50 |
Item31 |
10 |
Item32 |
20 |
Item33 |
30 |
Item34 |
40 |
3) We have four users assigned to access
an item
User 1 |
||
Level1 |
Level2 |
Level3 |
Item11 |
Item21 |
Item31 |
Item32 |
||
Item22 |
Item33 |
|
Item34 |
||
User 2 |
||
Level1 |
Level2 |
Level3 |
Item11 |
Item21 |
Item31 |
Item32 |
||
Item22 |
Item33 |
|
Item34 |
||
User 3 |
||
Level1 |
Level2 |
Level3 |
Item11 |
Item21 |
Item31 |
Item32 |
||
Item22 |
Item33 |
|
Item34 |
||
User 4 |
||
Level1 |
Level2 |
Level3 |
Item11 |
Item21 |
Item31 |
Item32 |
||
Item22 |
Item33 |
|
Item34 |
Now, we need to Model Power BI RLS to
show the data below as step 1
User 1 |
||
Level1 |
Level2 |
Level3 |
Item11 |
Item21 |
Item31 |
Item32 |
||
Item22 |
Item33 |
|
Item34 |
||
User 2 |
||
Level1 |
Level2 |
Level3 |
Item11 |
Item21 |
Item31 |
Item32 |
||
Item22 |
Item33 |
|
Item34 |
||
User 3 |
||
Level1 |
Level2 |
Level3 |
Item11 |
Item21 |
Item31 10 |
Item32 |
||
Item22 |
Item33 |
|
Item34 |
||
User 4 |
||
Level1 |
Level2 |
Level3 |
Item11 |
Item21 |
Item31 |
Item32 20 |
||
Item22 |
Item33 |
|
Item34 |
Please note that the number with green colour is correct only.
In addition, we want to hide whatever
items that are not allowed to show.
User 1 |
||
Level1 |
Level2 |
Level3 |
Item11 |
-- |
-- |
-- |
||
-- |
-- |
|
-- |
||
User 2 |
||
Level1 |
Level2 |
Level3 |
-- |
Item21 |
-- |
-- |
||
-- |
-- |
|
-- |
||
User 3 |
||
Level1 |
Level2 |
Level3 |
-- |
Item21 |
Item31 10 |
-- |
||
-- |
-- |
|
-- |
||
User 4 |
||
Level1 |
Level2 |
Level3 |
-- |
-- |
-- |
Item32 20 |
||
-- |
-- |
|
-- |
Analysis
There are a few challenges:
1) It is not a simple profile-based RLS,
as we can't directly connect an item to the fact table. Instead, it is via
hierarchy. We should see the result from the item that the user can access, but
we also need to add all underlying items.
2) It is not a simple hierarchical RLS,
as each user can only access more than one item. So the traditional solution
with DAX path can't work either.
3) Assuming that the hierarchy is big
and deep, pure DAX may not work due to poor performance.
Solutions
To resolve these challenges above, we
have introduced the extended hierarchy with parents and all their listed
children. It is derived from the hierarchy with reclusive functions such as CTE
with SQL server.
ItemParent |
ItemChild |
Item11 |
Item11 |
Item11 |
Item21 |
Item11 |
Item22 |
Item11 |
Item31 |
Item11 |
Item32 |
Item11 |
Item33 |
Item11 |
Item34 |
Item21 |
Item21 |
Item21 |
Item31 |
Item21 |
Item32 |
Item22 |
Item22 |
Item22 |
Item33 |
Item22 |
Item34 |
Item31 |
Item31 |
Item32 |
Item32 |
Item33 |
Item33 |
Item34 |
Item34 |
This table will provide a bridge between
the user profile and a hierarchy.
Implementation
1)
Create
all tables listed below
3)
Define
RLS as simply as below
[Email] = USERPRINCIPALNAME()
4)
Test
result
Dynamic data mask for
column, or hide items is impossible for Power BI
1)
In
order to hide column, we need to use DAX to check whether this item is in
UserItem table, if yes, then show, otherwise then hidden.
Show =
VAR vShow
=
CALCULATE (
INT ( NOT
ISEMPTY ( 'UserItem'
) ),
TREATAS ( VALUES
( 'Item'[Level1] ), UserItem[Item]
)
)
RETURN
IF ( vShow,
MAX ( 'Item'[Level1]
), "--" )
2) However, it is always not hidden, as the
security filter does NOT apply. Then we can add security directly in DAX as
below
Show =
VAR vShow
=
CALCULATE (
INT ( NOT
ISEMPTY ( 'UserItem'
) ),
TREATAS ( VALUES
( 'Item'[Level1] ), UserItem[Item]
),
FILTER ( 'User', User[Email] = USERPRINCIPALNAME () )
)
RETURN
IF ( vShow,
MAX ( 'Item'[Level1]
), "--" )
But it doesn’t work due to the error below
3) The only waywe can do is to make it as a measure, however, but then we need to Power to have Row context as below, it doesn’t make big sense anymore.
4) What was the real reason for Power BI?
As the error message indicates, you cannot use USERNAME or
USERPRINCIPALNAME within a calculated column or a calculated table. This is for
the same reason that you can't reference slicer selections in calculated
columns/tables. I.e., the model is
computed once when the data is loaded and not every time you change a
slicer or visit with a new user. Calculated
columns are pre-calculated. This is
why the security can’t apply for calculated columns at all.
On the other hand, Cognos has no problem handling this requirement,
as Parameters-map provides functionality to dynamically get values, then send
newly generated requests to either database ( CQM), or cash (DQM) to get the requested result. Please check https://yinsolutions.blogspot.com/2020/08/using-cognos-framework-manager.html for detail.
No comments:
Post a Comment