1 Objective
Maintaining security roles for large group of end users is always a challenging task. Hence it is required to develop solution with dynamic reporting security. In this case security requirements are based on OrgUnit hierarchy hence solution is for BW reports based on OrgUnit hierarchy.
Main idea is if A OrgUnit is assigned to user U1, then he should be able to access data of this OrgUnit & all child OrgUnits.
2 Brief Overview of Solution
Following are the parts of solution for making reporting security dynamic:
Z Table in ECC
This is required for maintaining ad-hoc OrgUnit assignments for accessing data for additional OrgUnits temporarily. This Z table will be loaded in a DSO in BW via table based datasource.
Extractor for OrgUnit Assignments
This extractor will bring OrgUnit assignments from HRP1001 & PA0001 for two type of users. HRP1001 will give OrgUnit assignments of users who are incharge of specific OrgUnits & PA0001 (Only specific designations will be considered based on requirements) will provide OrgUnit assignments of specific employees.
DSO in BW
This DSO will store data from two datasources mentioned above. This DSO will be referred by BW queries on run time to manage security.
BEx Variables
For making this dynamic security work, 2 Variables are added on OrgUnit:
Input Ready
This variable V1 is used in “Default Values” section in filter pane. For this variable we have implemented a standard BADI for restricting F4 help values based on security.
Customer Exit (No Input Ready)
This variable V2 is used in “Characteristic Restrictions” section of filter pane. It is customer exit variable, non-ready to input and to be processes in i_Step = 2. This variable will read values from other OrgUnit input ready variable and following is its logic flow:
If V1 variable is kept blank, this customer exit will be populated with authorized values of OrgUnit for the current user. Else if something is selected in V1, it will be validated in V2 and processed accordingly. More details in following sections of the document.
3 Z Table in ECC
This Z table is required to maintain ad-hoc OrgUnit assignments for reporting data at higher levels. For e.g. an end user may need to report data to (C level or Partner level) sometimes, for that he may need access to reports on OrgUnits which are not assigned to him in actual OrgUnit hierarchy in ECC, in these cases that user will be assigned required OrgUnits for specific period of time.
Maintenance generator is created for the table and table will be maintained via tcode and tcode will be assigned in security roles of specific people of application support team only.
Following is the structure of the table:
Here, start & end date will restrict the access to specific ad-hoc OrgUnit assignment to specific period of time.
3.1 Tcode for table maintenance
Maintenance Generator Created for table for maintenance followed by creation of tcode for its maintenance in SE93:
There is a table based generic datasource on this table.
4 Extractor for OrgUnit Assignments
This is a function module based generic datasource, based on client specific OrgUnit configuration it queries on HRP1001 table with filters on otype, sclas, rsign & relat to bring one set of users & corresponding OrgUnit assignments. Second set of reporting users is derived based on PA0001 based on senior executives or partners.
4.1 Extract structure for this datasource
4.2 Code Snippets
4.2.1 Starting Select Query
4.2.2 Middle Part 1
4.2.3 Middle Part 2
4.2.4 Last Part
5 DSO in BW
DSO in BW is mapped from both the datasources discussed in sections 3 & 4.
5.1 DSO Structure
6 BEx Variables
6.1 Input Ready Variable
We have implemented BADI for restricting F4 help values of this variables based on current user executing the report via lookup on security DSO. This variable is used in “Default Values” section of fiter pane.
6.1.1 Location of BADI
SPRO >> SAP NETWEAVER >> Business Warehouse >> Enhancements >> BAdI: Restricting the Value Help in the Variables Screen
6.1.2 Process of Implementing BADI for F4 Help Restriction
Clicking on highlighted part of above screen shot will take you to following screen:
Hit create button in above screen & then enter first two parameter (Name of new object) following pop-up-
Enhancement Implementation will be created:
Click on highlighted area in screenshot above “Implementing Class”-
Since we need to apply F4 help restriction on Hierarchy Node variable, we need to select second method in screenshot above (*_NODE).
- Code
Following code has been written in that method:
****************Declaration***************************************************************************
DATA: l_s_node LIKE LINE OF c_t_node,
l_s_hier LIKE LINE OF c_t_hierarchy.
DATA: lv_curr_user TYPE sy-uname,
lv_date TYPE sy-datum,
lv_nodeid TYPE rshienodid,
lv_node TYPE rsshnodename.
DATA: it_secd TYPE TABLE OF Active Table of BW DSO,
wa_secd TYPE Active Table of BW DSO.
DATA: it_orgeh TYPE TABLE OF H Table of Orgunit,
wa_orgeh TYPE H Table of Orgunit,
wa_orgeh1 TYPE H Table of Orgunit.
FIELD-SYMBOLS: <fs_orgeh> TYPE H Table of Orgunit,
<fs_orgeh1> TYPE H Table of Orgunit.
ASSIGN wa_orgeh TO <fs_orgeh>.
ASSIGN wa_orgeh1 TO <fs_orgeh1>.
*********************************************************************************************************
IF i_vnam EQ 'V1 <Technical Name of Input Ready Variable'.
CALL FUNCTION 'RSEC_GET_USERNAME'
IMPORTING
e_username = lv_curr_user.
CLEAR: lv_date.
lv_date = sy-datum.
SELECT *
FROM Active Table of DSO in BW
INTO TABLE it_secd
WHERE /bic/rmuname = lv_curr_user
AND validfrom LE lv_date
AND validto GE lv_date.
CLEAR wa_secd.
IF it_secd[] IS NOT INITIAL.
SELECT *
FROM H Table of OrgUnit
INTO TABLE it_orgeh
WHERE objvers = 'A'
AND datefrom LE lv_date
AND dateto GE lv_date.
LOOP AT it_secd INTO wa_secd.
CLEAR: l_s_node.
l_s_node-nodename = wa_secd-orgunit.
******Find if OrgUnit is Leaf Node or not.
READ TABLE it_orgeh ASSIGNING <fs_orgeh>
WITH KEY nodename = l_s_node-nodename.
IF sy-subrc = 0.
CLEAR: lv_nodeid.
lv_nodeid = <fs_orgeh>-nodeid.
READ TABLE it_orgeh ASSIGNING <fs_orgeh1>
WITH KEY parentid = lv_nodeid.
IF sy-subrc EQ 0. """This will mean, given OrgUnit is not leaf node
l_s_node-niobjnm = 'ORGUNIT'. ""This is required only if OrgUnit is not Leaf node
"l_s_node-niobjnm = I_IOBJNM.
ENDIF.
ENDIF.
APPEND l_s_node TO c_t_node.
ENDLOOP.
ENDIF.
ENDIF.
*********************************************************************************************************
6.1.3 Expected output of F4 Restriction based on Sample Values
Blue colored OrgUnits are assigned to user in BW DSO. Hierarchy shown on left side is main OrgUnit hierarchy and the one on right side is after applying code.
Using F4 help restriction, user can’t make selection on those strands of hierarchy which doesn’t have any of assigned OrgUnits, but he can still select OrgUnit which is parent of OrgUnit(s) assigned to him. To take care of this scenario, we created customer exit variable (i_step = 2), more details in next section.
6.2 Customer Exit (No Input Ready)
This is non-input ready customer exit variable to be processed in i_step = 2. This variable is used in “Characteristic Restrictions” section of Filter pane.
As discussed in last part of previous section, even after applying F4 help restriction, user can still select OrgUnit which is parent of OrgUnit he is assigned to. These kind of scenarios are handled in this customer exit code. Basically following are two reasons of having this additional variable on OrgUnit:
- a) If user selects nothing in “input ready variable”, then this customer exit will pass all OrgUnits from security DSO in BW in second Variable V2. Which means, user will see data only for the OrgUnits assigned to him.
- b) Validate selection made by user in variable V1. If user has selected something he is not authorized for then pass error.
6.2.1 Code of the Customer Exit (Class à Method)
Code is not written directly in CMOD, but instead using interface class in CMOD, code is written in class specific to the variable V2.
**************************************************************************************
METHOD Interface Class~execute.
*"*"Lokale Schnittstelle:
*" IMPORTING
*" VALUE(I_VNAM) LIKE RSZGLOBV-VNAM
*" VALUE(I_VARTYP) LIKE RSZGLOBV-VARTYP
*" VALUE(I_IOBJNM) LIKE RSZGLOBV-IOBJNM
*" VALUE(I_S_COB_PRO) TYPE RSD_S_COB_PRO
*" VALUE(I_S_RKB1D) TYPE RSR_S_RKB1D
*" VALUE(I_PERIV) TYPE RRO01_S_RKB1F-PERIV
*" VALUE(I_T_VAR_RANGE) TYPE RRS0_T_VAR_RANGE
*" VALUE(I_STEP) TYPE I DEFAULT 0
*" EXPORTING
*" VALUE(E_T_RANGE) TYPE RSR_T_RANGESID
*" VALUE(E_MEEHT) LIKE RSZGLOBV-MEEHT
*" VALUE(E_MEFAC) LIKE RSZGLOBV-MEFAC
*" VALUE(E_WAERS) LIKE RSZGLOBV-WAERS
*" VALUE(E_WHFAC) LIKE RSZGLOBV-WHFAC
*" VALUE(E_VAR_PROCESSED) TYPE C
*" CHANGING
*" VALUE(C_S_CUSTOMER) TYPE RRO04_S_CUSTOMER OPTIONAL
*"----------------------------------------------------------------------
*-----------------------------------------------------------------------
* Data Declaration
*-----------------------------------------------------------------------
DATA: l_s_range TYPE rsr_s_rangesid,
l_s_var_range TYPE rrrangeexit.
FIELD-SYMBOLS: <fs_var_range> TYPE rrrangeexit.
ASSIGN l_s_var_range TO <fs_var_range>.
DATA: lv_curr_user TYPE sy-uname,
lv_date TYPE sy-datum,
lv_nodeid TYPE rshienodid,
lv_node TYPE rsshnodename,
lv_level TYPE rstlevel,
lv_message TYPE string,
lv_count TYPE i VALUE 0,
lv_flag TYPE i VALUE 0,
lv_check TYPE i VALUE 0.
***** Internal Table for Security DSO
DATA: it_secd TYPE TABLE OF Active table of security DSO,
wa_secd TYPE Active table of security DSO.
***** Internal Table for Hierarchy Table
DATA: it_orgeh TYPE TABLE OF H Table of OrgUnit,
it_allwd TYPE TABLE OF H Table of OrgUnit,
wa_orgeh TYPE H Table of OrgUnit,
wa_orgeh1 TYPE H Table of OrgUnit,
wa_orgeh2 TYPE H Table of OrgUnit,
wa_allwd TYPE H Table of OrgUnit,
wa_allwd1 TYPE H Table of OrgUnit,
wa_allwd2 TYPE H Table of OrgUnit.
FIELD-SYMBOLS: <fs_orgeh> TYPE H Table of OrgUnit,
<fs_orgeh1> TYPE H Table of OrgUnit.
ASSIGN wa_orgeh TO <fs_orgeh>.
ASSIGN wa_orgeh1 TO <fs_orgeh1>.
*-----------------------------------------------------------------------
* Constants Declaration
*-----------------------------------------------------------------------
CONSTANTS: c_x TYPE c VALUE 'X'.
*-----------------------------------------------------------------------
* Check the I_STEP = 1,2, or 3
*-----------------------------------------------------------------------
IF i_step = 2.
********Security
CALL FUNCTION 'RSEC_GET_USERNAME'
IMPORTING
e_username = lv_curr_user.
CLEAR: lv_date.
lv_date = sy-datum.
SELECT *
FROM Active table of security DSO
INTO TABLE it_secd
WHERE /bic/rmuname = lv_curr_user
AND validfrom LE lv_date
AND validto GE lv_date.
IF it_secd[] IS NOT INITIAL.
CLEAR wa_secd.
SELECT *
FROM H Table of OrgUnit
INTO TABLE it_orgeh
WHERE objvers = 'A'
AND datefrom LE lv_date
AND dateto GE lv_date.
SORT it_orgeh BY nodename.
READ TABLE i_t_var_range INTO l_s_var_range
WITH KEY vnam = 'V1 <Input Ready Variable as mentioned in section 6.1>'.
IF sy-subrc = 0.
lv_count = 0.
LOOP AT it_secd INTO wa_secd.
CLEAR: lv_node.
lv_node = wa_secd-OrgUnit.
READ TABLE it_orgeh INTO wa_orgeh
WITH KEY nodename = lv_node.
IF sy-subrc = 0.
APPEND wa_orgeh TO it_allwd.
ENDIF.
ENDLOOP.
SORT it_allwd BY tlevel ASCENDING.
READ TABLE it_allwd INTO wa_allwd INDEX 1. ""Sy-Subrc check not required, as we are reading first record in the table
lv_level = wa_allwd-tlevel.
CLEAR: lv_nodeid, lv_node.
LOOP AT i_t_var_range INTO l_s_var_range
WHERE vnam = 'V1 <Input Ready Variable as mentioned in section 6.1>'.
CLEAR: l_s_range, lv_nodeid, lv_node.
CLEAR: l_s_range, lv_node.
lv_node = l_s_var_range-low.
*********Authorization Check*****
CLEAR: wa_orgeh.
READ TABLE it_orgeh INTO wa_orgeh
WITH KEY nodename = lv_node.
IF sy-subrc = 0.
IF wa_orgeh-tlevel < lv_level. ""Check 1
lv_message = 'Not Authorized for OrgUnit'.
CONCATENATE lv_message l_s_var_range-low INTO lv_message SEPARATED BY space.
**********Information Message 1
CALL FUNCTION 'RRMS_MESSAGE_HANDLING'
EXPORTING
i_class = 'RSBBS'
i_type = 'A'
i_number = '000'
i_msgv1 = lv_message
EXCEPTIONS
dummy = 1
OTHERS = 2.
IF sy-subrc <> 0.
* Implement suitable error handling here
ENDIF.
CONTINUE. """Skip this Selection as user don't have authorization at this level.
*************************
ELSEIF wa_orgeh-tlevel = lv_level. ""Check 2
READ TABLE it_allwd INTO wa_allwd
WITH KEY nodename = lv_node.
IF sy-subrc NE 0.
""If Record not Found. If level of Selected OrgUnit selected is equal to lowest level of Authorized OrgUnits,
""then Selected OrgUnit should be present in IT_ALLWD. If not then it would mean not autorized.
lv_message = 'Not Authorized for OrgUnit'.
CONCATENATE lv_message l_s_var_range-low INTO lv_message SEPARATED BY space.
**********Information Message 2
CALL FUNCTION 'RRMS_MESSAGE_HANDLING'
EXPORTING
i_class = 'RSBBS'
i_type = 'A'
i_number = '000'
i_msgv1 = lv_message
EXCEPTIONS
dummy = 1
OTHERS = 2.
IF sy-subrc <> 0.
* Implement suitable error handling here
ENDIF.
CONTINUE. """Skip this Selection as user don't have authorization at this level.
ENDIF.
ELSE. """if level of Selected OrgUnit is greater than the lowest level of authorized OrgUnits i.e. Selected OrgUnit is in lower or child levels of Authorized OrgUnit.
" BREAK-POINT.
READ TABLE it_allwd INTO wa_allwd
WITH KEY nodename = lv_node.
IF sy-subrc NE 0. ""Record not Found, hence further checks are required
*****************************************
CLEAR: wa_orgeh, wa_orgeh1, wa_allwd1.
READ TABLE it_orgeh INTO wa_orgeh
WITH KEY nodename = lv_node.
IF sy-subrc = 0.
READ TABLE it_orgeh INTO wa_orgeh1
WITH KEY nodeid = wa_orgeh-parentid.
IF sy-subrc = 0.
READ TABLE it_allwd INTO wa_allwd1
WITH KEY nodename = wa_orgeh1-nodename.
IF sy-subrc = 0.
**********************""No Action Required. Selected value will be passed on to e_t_range based on following code
ELSE.
***WHILE*************************
lv_nodeid = wa_orgeh1-parentid.""Checking Parent OrgUnit
lv_flag = 0.
WHILE lv_flag = 0.
CLEAR: wa_orgeh2, wa_allwd2.
READ TABLE it_orgeh INTO wa_orgeh2
WITH KEY nodeid = lv_nodeid.""Checking Parent OrgUnit
IF sy-subrc = 0.
READ TABLE it_allwd INTO wa_allwd2
WITH KEY nodename = wa_orgeh2-nodename.
IF sy-subrc = 0.
lv_flag = 1.
lv_check = 1. ""Authorization Check Successful
EXIT.
ELSE.
IF wa_orgeh2-tlevel = 2. ""2 is the Lowest Level, level 1 contains ROOT node, which shall not be used in Z table.
lv_flag = 1.
lv_check = 0. ""Authorization Check Failed
EXIT.
ENDIF.
lv_nodeid = wa_orgeh2-parentid.
lv_flag = 0.
ENDIF.
ELSE.
lv_flag = 1.
lv_check = 0. ""Authorization Check Failed
ENDIF.
ENDWHILE.
***ENDWHILE***********************
IF lv_check = 0.""""""" Based on Check Carried out in WHILE.
*********************"""No Authorization for the selected OrgUnit
lv_message = 'Not Authorized for OrgUnit'.
CONCATENATE lv_message l_s_var_range-low INTO lv_message SEPARATED BY space.
**********Information Message 3
CALL FUNCTION 'RRMS_MESSAGE_HANDLING'
EXPORTING
i_class = 'RSBBS'
i_type = 'A'
i_number = '000'
i_msgv1 = lv_message
EXCEPTIONS
dummy = 1
OTHERS = 2.
IF sy-subrc <> 0.
* Implement suitable error handling here
ENDIF.
CONTINUE. """Skip this Selection as user don't have authorization at this level.
ENDIF.""""""" Based on Check Carried out in WHILE
ENDIF.
ENDIF.
ENDIF.
*****************************************
ENDIF.
ENDIF.
ENDIF.
********************************
l_s_range-sign = 'I'.
l_s_range-opt = 'EQ'.
l_s_range-low = l_s_var_range-low.
******Find if OrgUnit is Leaf Node or not.
READ TABLE it_orgeh ASSIGNING <fs_orgeh>
WITH KEY nodename = lv_node.
IF sy-subrc = 0.
CLEAR: lv_nodeid.
lv_nodeid = <fs_orgeh>-nodeid.
READ TABLE it_orgeh ASSIGNING <fs_orgeh1>
WITH KEY parentid = lv_nodeid.
IF sy-subrc EQ 0. """This will mean, given OrgUnit is not leaf node
""IF wa_secd-OrgUnit = 'ROOT'.
""l_s_range-high = '0HIER_NODE'.
""ELSE.
"BREAK-POINT.
l_s_range-high = 'ORGUNIT'. ""This is required only if OrgUnit is not Leaf node
""ENDIF.
ENDIF.
ENDIF.
APPEND l_s_range TO e_t_range.
lv_count = lv_count + 1.
ENDLOOP.
****************************************************************************************************************************
IF lv_count = 0.
********User didn't selected any valid value---using code E1_Part to pass authorized values
*---------COPY_E1_PART--------------------------------------------------------------
*""No Value passed in variable RMCN_ORGUNIT2, hence need to
*""populate it based on authorization
*-----------------------------------------------------------------------
LOOP AT it_secd INTO wa_secd.
CLEAR: l_s_range, lv_node.
l_s_range-sign = 'I'.
l_s_range-opt = 'EQ'.
l_s_range-low = wa_secd-OrgUnit.
lv_node = wa_secd-OrgUnit.
******Find if OrgUnit is Leaf Node or not.
READ TABLE it_orgeh ASSIGNING <fs_orgeh>
WITH KEY nodename = lv_node BINARY SEARCH.
IF sy-subrc = 0.
CLEAR: lv_nodeid.
lv_nodeid = <fs_orgeh>-nodeid.
READ TABLE it_orgeh ASSIGNING <fs_orgeh1>
WITH KEY parentid = lv_nodeid.
IF sy-subrc EQ 0. """This will mean, given OrgUnit is not leaf node
""IF wa_secd-OrgUnit = 'ROOT'.
""l_s_range-high = '0HIER_NODE'.
""ELSE.
"BREAK-POINT.
l_s_range-high = 'ORGUNIT'. ""This is required only if OrgUnit is not Leaf node
""ENDIF.
ENDIF.
ENDIF.
APPEND l_s_range TO e_t_range.
ENDLOOP.
* Don't remove
e_var_processed = c_x.
ENDIF.
****************************************************************************************************************************
ELSE.
*---------E1_PART--------------------------------------------------------------
*""No Value passed in variable V1, hence need to
*""populate it based on authorization
*-----------------------------------------------------------------------
LOOP AT it_secd INTO wa_secd.
CLEAR: l_s_range, lv_node.
l_s_range-sign = 'I'.
l_s_range-opt = 'EQ'.
l_s_range-low = wa_secd-OrgUnit.
lv_node = wa_secd-OrgUnit.
******Find if OrgUnit is Leaf Node or not.
READ TABLE it_orgeh ASSIGNING <fs_orgeh>
WITH KEY nodename = lv_node BINARY SEARCH.
IF sy-subrc = 0.
CLEAR: lv_nodeid.
lv_nodeid = <fs_orgeh>-nodeid.
READ TABLE it_orgeh ASSIGNING <fs_orgeh1>
WITH KEY parentid = lv_nodeid.
IF sy-subrc EQ 0. """This will mean, given OrgUnit is not leaf node
""IF wa_secd-OrgUnit = 'ROOT'.
""l_s_range-high = '0HIER_NODE'.
""ELSE.
"BREAK-POINT.
l_s_range-high = 'ORGUNIT'. ""This is required only if OrgUnit is not Leaf node
""ENDIF.
ENDIF.
ENDIF.
APPEND l_s_range TO e_t_range.
ENDLOOP.
* Don't remove
e_var_processed = c_x.
ENDIF.
ELSE.
CALL FUNCTION 'RRMS_MESSAGE_HANDLING'
EXPORTING
i_class = 'RSBBS'
i_type = 'A'
i_number = '000'
i_msgv1 = 'No Org Unit assigned for this User'
EXCEPTIONS
dummy = 1
OTHERS = 2.
IF sy-subrc <> 0.
* Implement suitable error handling here
ENDIF.
ENDIF.
ENDIF.
**************************************************************************************
7 Conclusion
7.1 Dynamic Security for BW Reporting
- a) Simple roles are required for users restricting them to specific Infoarea, Infoproviders & BEx queries (using Wild Characters).
- b) In security roles need not maintain OrgUnit values, which may change based on employee’s position changes.
- c) If a User is moved in OrgUnit hierarchy in ECC based on HR actions (Promotion or Transfer), this change will come to BW DSO automatically and end users reporting authorizations are automatically taken care of.
- d) If any user needs ad-hoc OrgUnit assignments, just need to maintain Z table with validity dates for ad-hoc assignment and rest all taken care post loading the table in BW.
7.2 Brief of Fucntioning
If user doesn’t enter anything in input ready variable, second customer exit variable will filter report based on OrgUnits assigned to the user.
If user selects something in first input ready variable (where F4 help restriction is applied), second variable will validate security based on OrgUnit assignments.
8 References
F4 Help Restriction
http://wiki.scn.sap.com/wiki/display/BI/F4+BADI+RESTRICTION+NODE