You realize that your code gives users access to data which they are not authorized to see. To amend this you first let your code read from CDS view entities for which CDS access controls are defined. Then you implement an explicit authority check to be able to distinguish between data the user is not authorized to see and data that does not exist at all.
Task 1: Copy Template (Optional)
Copy the template class. If you finished the previous exercise, you can skip this task and continue editing your class ZCL_##_SOLUTION.
Steps
Copy class /LRN/CL_S4D401_ITS_SEC_KEY to a class in your own package (suggested name: ZCL_##_SOLUTION, where ## stands for your group number).
In the Project Explorer view, right-click class /LRN/CL_S4D401_ITS_SEC_KEY to open the context menu.
From the context menu, choose Duplicate ....
Enter the name of your package in the Package field. In the Name field, enter the name ZCL_##_SOLUTION, where ## stands for your group number.
Adjust the description and choose Next.
Confirm the transport request and choose Finish.
Activate the copy.
Press Ctrl + F3 to activate the class.
Task 2: Analyze Authorizations
Starting from database table /LRN/CARRIER, find the CDS access control that protects carrier data against unauthorized access. Analyze the CDS access control to find out which authorizations a user needs to display carrier data.
Steps
Display the definition of database table /LRN/CARRIER.
Press Ctrl + Shift + A and enter /LRN/CARRIER.
Analyze the Where-used list for this database table .
In the definition of database table /LRN/CARRIER, right-click on /LRN/CARRIER and choose Get Where-Used List.
Alternatively, click on /LRN/CARRIER and press Ctrl + Shift + G.
In the Search view below the editor, Choose the funnel icon to open the filter dialog.
Place the cursor in the Object Type(s): field and press Ctrl + Space to open a value help dialog.
From the suggestion list, choose value DDLS/DF (Data Definition) and choose Apply.
Analyze the Where-used list for these CDS views.
In the Search view, right-click on /LRN/I_CARRIER (Data Definition) and choose Get Where-Used List.
Alternatively, click on /LRN/I_CARRIER (Data Definition) and press Ctrl + Shift + G.
If the Search view below the editor displays too many hits, choose the funnel icon to open the filter dialog.
Place the cursor in the Object Type(s): field and press Ctrl + Space to open a value help dialog.
From the suggestion list, choose value DCLS/DL (Access Control) and choose Apply.
Open the definition of access control /LRN/I_CARRIER.
In the Search view, double click /LRN/I_CARRIER (Access Control).
The authorization object name is the first input parameter for function pfcg_auth.
The other input parameters supply values for the authorization fields of the authorization object.
Open the definition of the authorization object(s) to analyze the meaning of the activity values.
Hold down the Ctrl key and choose the name of the authorization object (that is, the mention of /LRN/CARR in the call of function pfcg_auth).
The possible activity values are listed under Permitted Activities.
Task 3: Use CDS Access Control
In the constructor method of local class LCL_CARRIER, replace database table /LRN/CARRIER with CDS view entity /LRN/I_Carrier, which is protected by CDS access control /LRN/I_CARRIER. Test the code with different carrier IDs as constructor input.
Steps
Open the implementation of the constructor method of local class LCL_CARRIER.
Return to the code of your global class ZCL_##_SOLUTION, where ## is your group number.
Either search on the Local Types tab or expand LCL_CARRIER → CONSTRUCTOR in the Outline view and double-click CONSTRUCTOR.
Adjust the SELECT statement. In the FROM clause, replace database table /DMO/CARRIER with the CDS view entity you just found.
Adjust the code as follows:
12345
SELECT SINGLE
* FROM /lrn/carrier
FROM /LRN/I_Carrier
Fix the syntax errors. Replace the table field names with the corresponding CDS view element names.
Adjust the code as follows:
12345678910111213
* SELECT SINGLE
* FROM /lrn/carrier
* FIELDS concat_with_space( carrier_id, name, 1 ) , currency_code
* WHERE carrier_id = @i_carrier_id
* INTO ( @me->name, @me->currency_code ).
SELECT SINGLE
FROM /lrn/i_carrier
FIELDS concat_with_space( airlineid, name, 1 ) , currencycode
WHERE airlineid = @i_carrier_id
INTO ( @me->name, @me->currency_code ).
Activate and test your global class as console app.
Press Ctrl + F3.
Press F9.
In your global class, change the value of local constant c_carrier_id to UA.
Switch to the Global Class tab.
Adjust the code as follows:
1234
* CONSTANTS c_carrier_id TYPE /dmo/carrier_id VALUE 'LH'.
CONSTANTS c_carrier_id TYPE /dmo/carrier_id VALUE 'UA'.
Activate and test your global class as console app.
Press Ctrl + F3.
Press F9.
Task 4: Implement an AUTHORITY-CHECK Statement
In the constructor method of local class LCL_CARRIER, implement an AUTHORITY-CHECK statement and raise exception CX_ABAP_AUTH_CHECK_EXCEPTION if the flight carrier exists but the user is not authorized for this carrier.
Steps
Open the implementation of the constructor method of local class LCL_CARRIER.
Return to the code of your global class ZCL_##_SOLUTION, where ## is your group number.
Either search on the Local Types tab or expand LCL_CARRIER → CONSTRUCTOR in the Outline view and double-click CONSTRUCTOR.
Adjust the SELECT statement to read from database table database table /DMO/CARRIER again.
Adjust the code as follows:
12345678910111213
* SELECT SINGLE
* FROM /lrn/i_carrier
* FIELDS concat_with_space( airlineid, name, 1 ) , currencycode
* WHERE airlineid = @i_carrier_id
* INTO ( @me->name, @me->currency_code ).
SELECT SINGLE
FROM /lrn/carrier
FIELDS concat_with_space( carrier_id, name, 1 ) , currency_code
WHERE carrier_id = @i_carrier_id
INTO ( @me->name, @me->currency_code ).
Implement an AUTHORITY-CHECK statement that is performed if the read access was successful, that is if exception CX_ABAP_INVALID_VALUE has not been raised. Use the authorization object, authorization fields and values you found earlier.
Adjust the code as follows:
1234567891011121314
IF sy-subrc <> 0.
RAISE EXCEPTION TYPE cx_abap_invalid_value.
ENDIF.
AUTHORITY-CHECK
OBJECT '/LRN/CARR'
ID '/LRN/CARR' FIELD i_carrier_id
ID 'ACTVT' FIELD '03'.
IF sy-subrc <> 0.
ENDIF.
If the user does not have the display authorization for the requested airline ID raise exception CX_ABAP_AUTH_CHECK_EXCEPTION.
Adjust the code as follows:
12345678910
AUTHORITY-CHECK
OBJECT '/LRN/CARR'
ID '/LRN/CARR' FIELD i_carrier_id
ID 'ACTVT' FIELD '03'.
IF sy-subrc <> 0.
RAISE EXCEPTION TYPE cx_abap_auth_check_exception.
ENDIF.
Use a quick fix to add the exception to the RAISING clause of the method definition.
In the RAISE EXCEPTION statement, click on cx_abap_auth_check_exception and press Ctrl + 1.
Alternatively, choose the light-bulb icon left from the code row with the syntax warning.
From the list of suggested quick fixes, choose Add raising declaration.
As result, the definition of the CONSTRUCTOR method should now look like this:
12345678
METHODS constructor
IMPORTING
i_carrier_id TYPE /dmo/carrier_id
RAISING
cx_abap_invalid_value
cx_abap_auth_check_exception.
In your global class, extend the TRY … ENDTRY control structure with a CATCH block for the new exception. Issue a suitable text to indicate that user is not authorized to display the carrier.
Adjust the code as follows:
123456789101112
TRY.
DATA(carrier) = NEW lcl_carrier( i_carrier_id = c_carrier_id ).
out->write( name = `Carrier Overview`
data = carrier->get_output( ) ).
CATCH cx_abap_invalid_value.
out->write( | Carrier { c_carrier_id } does not exist | ).
CATCH cx_abap_auth_check_exception.
out->write( | No authorization to display carrier { c_carrier_id } | ).
ENDTRY.
Similarly, extend the TRY … ENDTRY control structure in the class_setup method of your local unit test class. Use the same implementation as in the catch block for exception CX_ABAP_INVALID_VALUE.
Note
This is just a workaround. Later I this course, you will learn how to handle several exceptions in the same catch block.Adjust the code as follows:
123456789
TRY.
the_carrier = NEW lcl_carrier( i_carrier_id = some_flight_data-carrier_id ).
CATCH cx_abap_invalid_value.
cl_abap_unit_assert=>fail( `Unable to instantiate lcl_carrier` ).
CATCH cx_abap_auth_check_exception.
cl_abap_unit_assert=>fail( `Unable to instantiate lcl_carrier` ).
ENDTRY.
Activate and test your global class as console app. Ensure that your code displays the new text on the console.
Press Ctrl + F3.
Press F9.
In your global class, change the value of local constant c_carrier_id back to LH.
Switch to the Global Class tab.
Adjust the code as follows:
1234
* CONSTANTS c_carrier_id TYPE /dmo/carrier_id VALUE 'UA'.
CONSTANTS c_carrier_id TYPE /dmo/carrier_id VALUE 'LH'.
Activate and test your global class as console app. Ensure that the code displays the carrier and flight details as before.
Press Ctrl + F3.
Press F9.