-2

I am trying to achieve flattening a table into an array (or structure). My implementation is in SAP ABAP; however, I believe the question is code agnostic. I am wondering if there is an algorithm I'm not aware of or simple solution for what I'm trying to achieve. I will accept any language or pseudocode as an acceptable answer.

I am selecting from a "User Restrictions" (i.e., lt_restrictions) table stored on a database, e.g.:

enter image description here

This is needed for an SAP BSP MVC web app. When the app loads, I am doing user authorization checks to populate a "User Roles" (i.e., mt_user_roles) local table/array (i.e., attribute on an MVC model class).

The authorization checks involve routine ABAP -- using IF statements to conditionally populate the "User Roles" table, e.g.:

AUTHORITY-CHECK OBJECT 'PROGRAM_MANAGER_AUTH' ID 'xxx' FIELD 'yyy'.
IF ( sy-subrc = 0 ).
  APPEND VALUE #( low = 'program_manager' ) TO me->mt_user_roles.
ENDIF.

If the user has both e.g., program manager AND entry user authorizations, this "User Roles" table/array would look like this:

user roles table

With the two tables above, if the user has program manager and entry user roles, the only restriction is convert; therefore, I need to populate a "Restriction List" (i.e., ms_restrictions) like the following:

enter image description here

Otherwise, if the user has e.g., only the entry user role:

enter image description here ...

...there are 2 restrictions (release AND convert), so I would need to populate a "Restriction List" (i.e., ms_restrictions) like this:

enter image description here

I feel like there is a standard algorithm I could use or perhaps this could be done with SQL in a SELECT statement with my database table WHERE restriction = NULL (e.g., WHERE save = ' ') or using a SELECT ... WHERE x IN clause, perhaps, if the "Restrictions List" was a RANGE table?

I should mention, I already have an undesirable, partial solution which involves looping through the database table and having several IF statements for each restriction condition. "Restriction List" is named ms_restrictions, it is a class attribute ABAP STRUCTURE type (for ABAP newbies, this is basically a 1-D sized array):

LOOP AT lt_restrictions INTO DATA(ls_restriction). 
    LOOP AT mt_user_roles INTO DATA(ls_user_role).
        "Check save restriction 
        IF ( 
            ls_restriction-role = ls_user_role-role AND
            ls_user_role = 'save'
        ).
            ms_restrictions-save = 'x'.
        ENDIF.
       "Check release restriction
       IF ( 
            ls_restriction-role = ls_user_role-role AND
            ls_user_role = 'release'
        ).
            ms_restrictions-release = 'x'.
        ENDIF.
       "Check other restrictions
       "......

     ENDLOOP.
ENDLOOP.
            

The problem with the solution above is I have to write an IF statement for each restriction. More so, it doesn't take into account whether there are conflicts between 2 or more role restrictions, e.g., program manager has only 1 restriction while entry user has 2. My logic above could be extended to take this into account with -- well -- even more IF statements :-( .. I'm wondering if there's a different approach I can take?

Also, I should mention. The resulting "Restrictions List", i.e., mt_restrictions, is needed in my view for disabling HTML buttons, e.g.:

<button value = 'Save'    disabled = '//model/mt_restrictions-save' />
<button value = 'Release' disabled = '//model/mt_restrictions-release' />
<button value = 'Convert' disabled = '//model/mt_restrictions-convert' />
<button ... />
Jim22150
  • 511
  • 2
  • 8
  • 22
  • This is a pretty broad question. I'm guessing someone downvoted you because you haven't really tried anything, but we've all be stuck, so I'll try. To help. A little clarification would be nice: Where are you getting the data for the relationship between user and roles? Also, are you trying to populate the User Roles Table and THEN use that to get permissions? If so, is there any reason you can't directly create the permissions table/array? – CVerica May 19 '21 at 23:05
  • 2
    You can't just go tagging all existing languages. You need focus. – Rodrigo Rodrigues May 19 '21 at 23:49
  • @cverica I expanded the details of my question, I hope this helps – Jim22150 May 19 '21 at 23:57
  • @RodrigoRodrigues, thank you for your suggestion to correct. I've made the corrections that I believe will address your concern. Would you please be able to vote to reopen this question? With the fixes I just made, I believe this is a good computer science question that is code agnostic. Thank you. – Jim22150 May 20 '21 at 02:59
  • @Snow would you please be able to vote to reopen this question? With the fixes I just made, I believe this is a good computer science question that is code agnostic. Thank you – Jim22150 May 20 '21 at 03:12
  • @azurefrog, would you please be able to vote to reopen this question? With the fixes I just made, I believe this is a good computer science question that is code agnostic. Thank you – Jim22150 May 20 '21 at 03:13
  • Is there any reason why you don't want to use the SAP standard permission system based on PFCG roles and the `AUTHORITY-CHECK` keyword? It's generally a bad idea to invent your own parallel permission infrastructure when you are using an environment which already got one. – Philipp May 20 '21 at 11:22
  • @Philipp, this question was originally supposed to be more code agnostic -- I was hoping to make it a general computer science question instead of an SAP ABAP specific one; but, because there were concerns about the question being to general, I've catered. Also, I am now using the ABAP AUTHORITY-CHECK keyword. Thank you for pointing that out. – Jim22150 May 20 '21 at 12:15
  • @Philipp with the change in place you suggested, do you think this is a worth while ABAP question? If so, would you please consider voting reopen it? – Jim22150 May 21 '21 at 12:24
  • @Jim22150 That depends. Would you accept a frame challenge answer which tells you to use SAP authorizations the way they are supposed to be used and not derive your own "virtual" roles from the results of authority checks? – Philipp May 21 '21 at 12:26
  • @philipp I'm not following your hypothetical condition. The authority checks are being used to determine whether or not to disable HTML tags. If you understand how MVC BSPs work, look at my last bit of code and how I bind a list of restriction rules to n amount of buttons. – Jim22150 May 22 '21 at 16:09

1 Answers1

0

A solution I came up with involves using ABAP <FIELD-SYMBOL>s data-types and an OpenSQL FOR ALL ENTRIES IN SELECT statement addition.

By using <FIELD-SYMBOL>s, you can avoid any hardcoded attribute and/or structure component names. Now, the restriction rules can grow without having to future modify any code.

By using FOR ALL ENTRIES IN, you can sync your Roles table with your Restrictions table. Adding this clause allows you to SELECT from your transparent Role/Restrictions database table INTO a table, only including the roles applicable from the roles found in the AUTH-CHECK (because mt_user_roles populates a new row for each true AUTH-CHECK).

The method is_standard_user( ) is where the AUTH-CHECKs occur. If it equals ABAP_TRUE, then all restrictions are applied -- assuming the ms_restrictions structure components' values all default to ABAP_TRUE.

Dynamically looping through each structure component of ms_restrictions, you can READ the restrictions table for corresponding value. If value equals SPACE, then this means the current row doesn't have a restriction. So mark the structure component value to SPACE (because it may have been marked ABAP_TRUE in a prior iteration) -- this addresses the "conflicting roles issue".

"perform authorization checks
IF ( NOT me->is_standard_user( ) ).

  "see if roles found
  IF ( lines( me->mt_user_roles ) > 0 ).

    "find pertaining restrictions for roles found in auth checks
    SELECT
      *
      FROM
        zmm_apr_roles
        FOR ALL ENTRIES IN    "select only rows applicable to auth-check
          @me->mt_user_roles
        WHERE
          role = @me->mt_user_roles-role
        INTO TABLE
          @DATA(lt_restrictions).

    "dynamically populate restriction list from multi-role restriction table
    DATA: lr_descr_struc TYPE REF TO data.
    DATA: lo_structdescr TYPE REF TO cl_abap_structdescr.
    CREATE DATA lr_descr_struc  LIKE ms_restrictions.
    lo_structdescr ?= cl_abap_structdescr=>describe_by_data_ref( p_data_ref = lr_descr_struc ).
    LOOP AT lo_structdescr->components ASSIGNING FIELD-SYMBOL(<lv_component>).
      ASSIGN COMPONENT <lv_component>-name OF STRUCTURE ms_restrictions TO FIELD-SYMBOL(<lv_field>).
      IF ( <lv_field> IS ASSIGNED ).
        "non-restricted value overwrites restricted value
        READ TABLE lt_restrictions WITH KEY (<lv_component>-name) = space TRANSPORTING NO FIELDS.
        IF ( sy-subrc = 0 ).
          <lv_field> = space.
        ENDIF.
      ENDIF.
    ENDLOOP.

  ENDIF.

ENDIF.

To add a new restriction, all that needs to be done is:

  • add new roles to zmm_apr_roles and the corresponding data-dictionary components
  • add new restrictions to corresponding components of ms_restrictions
Jim22150
  • 511
  • 2
  • 8
  • 22