OAUTH call from ABAP

OAUTH can be called from custom ABAP. The explanation is given in this formal SAP help file. But it is quite complex.

In the example program below we will use OAUTH to call SAP BTP CPI.

First in SE80 we create a OAUTH client profile named ZOAUTH_CLIENT_PROFILE_CPI:

Then the rest of the ABAP coding is according to the SAP help file, including the error handling on issues you might face.

*&--------------------------------------------------------------------*
*& Report Z_CALL_API_USING_OAUTH
*&--------------------------------------------------------------------*
*&
*&--------------------------------------------------------------------*

REPORT z_call_api_using_oauth.

PARAMETERS:
  zp_url   TYPE string                    LOWER CASE
                                          DEFAULT 'https://apimanagement.eu10.hana.ondemand.com/v1/api/hc/xxx/call_name',
  zp_sslid TYPE strustssl-applic          DEFAULT 'ANONYM',
  zp_profl TYPE oa2c_profiles-profile     DEFAULT 'ZOAUTH_CLIENT_PROFILE_CPI',
  zp_confg TYPE oa2c_client-configuration DEFAULT 'ZOAUTH_CLIENT_PROFILE_CPI'.

CONSTANTS:
  BEGIN OF zgcs_create_return,
    argument_not_found TYPE sy-subrc VALUE 1,
    plugin_not_active  TYPE sy-subrc VALUE 2,
    internal_error     TYPE sy-subrc VALUE 3,
    others             TYPE sy-subrc VALUE 4,
  END OF zgcs_create_return.

START-OF-SELECTION.

  " oData: restrict to two entries returned, via url
  DATA(zgv_api_url) = |{ zp_url }?$top=2|.

  cl_http_client=>create_by_url( EXPORTING  url                = zgv_api_url
                                            ssl_id             = zp_sslid
                                 IMPORTING  client             = DATA(zlo_http_client)
                                 EXCEPTIONS argument_not_found = zgcs_create_return-argument_not_found
                                            plugin_not_active  = zgcs_create_return-plugin_not_active
                                            internal_error     = zgcs_create_return-internal_error
                                            OTHERS             = zgcs_create_return-others ).

  CASE sy-subrc.
    WHEN zgcs_create_return-argument_not_found.
      MESSAGE 'Argument not found when trying to create http client instance' TYPE 'E'.
    WHEN zgcs_create_return-plugin_not_active.
      MESSAGE 'Plugin not active for creation of http client instance' TYPE 'E'.
    WHEN zgcs_create_return-internal_error.
      MESSAGE 'Internal error when trying to create http client instance' TYPE 'E'.
    WHEN zgcs_create_return-others.
      MESSAGE 'Generic error when trying to create http client instance' TYPE 'E'.
  ENDCASE.

  zlo_http_client->propertytype_logon_popup = zlo_http_client->co_disabled.

  TRY.
      DATA(zgo_oauth_client) = cl_oauth2_client=>create( i_profile       = zp_profl
                                                         i_configuration = zp_confg ).
    CATCH cx_oa2c_config_not_found.
      MESSAGE 'OAuth 2.0 Client Configuration not found' TYPE 'E'.
    CATCH cx_oa2c_config_profile_assign.
      MESSAGE 'OAuth 2.0 Client Config - Unassigned Profile' TYPE 'E'.
    CATCH cx_oa2c_kernel_too_old.
      MESSAGE 'OAuth 2.0 Client - Kernel too old' TYPE 'E'.
    CATCH cx_oa2c_missing_authorization.
      MESSAGE 'OAuth 2.0 Client missing authorization' TYPE 'E'.
    CATCH cx_oa2c_config_profile_multi.
      MESSAGE 'OAuth 2.0 Client Config - Profile assigned multiple times' TYPE 'E'.
  ENDTRY.

  " Set oAuth token to the http client
  TRY.
      zgo_oauth_client->set_token( io_http_client = zlo_http_client
                                   i_param_kind   = if_oauth2_client=>c_param_kind_header_field ).
    CATCH cx_oa2c_at_not_available
          cx_oa2c_at_expired.

      " When setting the token fails, first try and get a new token
      TRY.
          zgo_oauth_client->execute_cc_flow( ).
        CATCH cx_oa2c_badi_implementation.
          MESSAGE 'OAuth 2.0 Client BAdI Impl. Error' TYPE 'E'.
        CATCH cx_oa2c_not_supported.
          MESSAGE 'Not supported by Service Provider.' TYPE 'E'.
        CATCH cx_oa2c_not_allowed.
          MESSAGE 'OAuth 2.0 Client Runtime - Not Allowed' TYPE 'E'.
        CATCH cx_oa2c_prot_http_failure.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - HTTP Failure' TYPE 'E'.
        CATCH cx_oa2c_prot_other_error.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Other Error' TYPE 'E'.
        CATCH cx_oa2c_prot_unexpected_code.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Unexpected Code' TYPE 'E'.
        CATCH cx_oa2c_prot_http_forbidden.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - HTTP 403 - Forbidden' TYPE 'E'.
        CATCH cx_oa2c_prot_http_not_found.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - HTTP 404 - Not Found' TYPE 'E'.
        CATCH cx_oa2c_server_error.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Server Error' TYPE 'E'.
        CATCH cx_oa2c_temporarily_unavail.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Temporarily Unavailable' TYPE 'E'.
        CATCH cx_oa2c_unsupported_grant_type.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Unsupported Grant Type' TYPE 'E'.
        CATCH cx_oa2c_unauthorized_client.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Unauthorized Client' TYPE 'E'.
        CATCH cx_oa2c_invalid_scope.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Invalid Scope' TYPE 'E'.
        CATCH cx_oa2c_invalid_grant.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Invalid Grant' TYPE 'E'.
        CATCH cx_oa2c_invalid_client.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Invalid Client' TYPE 'E'.
        CATCH cx_oa2c_invalid_request.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Invalid Request' TYPE 'E'.
        CATCH cx_oa2c_invalid_parameters.
          MESSAGE 'OAuth 2.0 Client Runtime - Invalid Parameters' TYPE 'E'.
        CATCH cx_oa2c_secstore_adm.
          MESSAGE 'OAuth 2.0 Client Runtime - SecStore Administration' TYPE 'E'.
        CATCH cx_oa2c_secstore.
          MESSAGE 'OAuth 2.0 Client Runtime - Secstore' TYPE 'E'.
        CATCH cx_oa2c_protocol_exception.
          MESSAGE 'OAuth 2.0 Client Runtime - Protocol Exception' TYPE 'E'.
      ENDTRY.

      " Set oAuth token to the http client
      TRY.
          zgo_oauth_client->set_token( io_http_client = zlo_http_client
                                       i_param_kind   = if_oauth2_client=>c_param_kind_header_field ).
        CATCH cx_oa2c_at_not_available.
          MESSAGE 'oAuth 2.0: Acces token not available' TYPE 'E'.
        CATCH cx_oa2c_at_expired.
          MESSAGE 'Access Token has expired.' TYPE 'E'.
        CATCH cx_oa2c_at_profile_not_covered.
          MESSAGE 'Access token has expired.' TYPE 'E'.
        CATCH cx_oa2c_not_supported.
          MESSAGE 'Not supported by Service Provider.' TYPE 'E'.
        CATCH cx_oa2c_badi_implementation.
          MESSAGE 'OAuth 2.0 Client BAdI Impl. Error' TYPE 'E'.
        CATCH cx_oa2c_secstore.
          MESSAGE 'OAuth 2.0 Client Runtime - Secstore' TYPE 'E'.
        CATCH cx_oa2c_invalid_parameters.
          MESSAGE 'OAuth 2.0 Client Runtime - Invalid Parameters' TYPE 'E'.
        CATCH cx_oa2c_icf_error.
          MESSAGE 'Unknown error received from ICF.' TYPE 'E'.
      ENDTRY.

    CATCH cx_oa2c_at_profile_not_covered.
      MESSAGE 'Access token has expired.' TYPE 'E'.
    CATCH cx_oa2c_not_supported.
      MESSAGE 'Not supported by Service Provider.' TYPE 'E'.
    CATCH cx_oa2c_badi_implementation.
      MESSAGE 'OAuth 2.0 Client BAdI Impl. Error' TYPE 'E'.
    CATCH cx_oa2c_secstore.
      MESSAGE 'OAuth 2.0 Client Runtime - Secstore' TYPE 'E'.
    CATCH cx_oa2c_invalid_parameters.
      MESSAGE 'OAuth 2.0 Client Runtime - Invalid Parameters' TYPE 'E'.
    CATCH cx_oa2c_icf_error.
      MESSAGE 'Unknown error received from ICF.' TYPE 'E'.
  ENDTRY.

  " From here on handle the http client for the API interaction
  zlo_http_client->request->set_version( if_http_request=>co_protocol_version_1_0 ).
  DATA(zlo_rest_client) = NEW cl_rest_http_client( io_http_client = zlo_http_client ).

" Get data from API
  TRY.
      zlo_rest_client->if_rest_client~get( ).
      " Collect response received from the REST API
      DATA(zli_response) = zlo_rest_client->if_rest_client~get_response_entity( ).
      DATA(zgv_http_status_code) = zli_response->get_header_field( `~status_code` ).
      DATA(zgv_status_reason)    = zli_response->get_header_field( `~status_reason` ).
      DATA(zgv_response_data)    = zli_response->get_string_data( ).

      " Record the response of the interface
      IF zgv_http_status_code BETWEEN 200 AND 299.
        " Success
        MESSAGE 'Call was succesful' TYPE 'S'.
      ELSE.
        MESSAGE 'Call failed' TYPE 'E'.
      ENDIF.

      WRITE / 'Response'.
      WRITE / zgv_response_data.

      " Issues with REST client must not lead to a short-dump
    CATCH cx_rest_client_exception INTO DATA(zlx_rest_client).
      IF zlx_rest_client->if_t100_message~t100key IS NOT INITIAL.
        DATA zlv_message TYPE string.
        MESSAGE ID zlx_rest_client->if_t100_message~t100key-msgid
                 TYPE 'E'
                 NUMBER zlx_rest_client->if_t100_message~t100key-msgno
                   WITH zlx_rest_client->if_t100_message~t100key-attr1
                        zlx_rest_client->if_t100_message~t100key-attr2
                        zlx_rest_client->if_t100_message~t100key-attr3
                        zlx_rest_client->if_t100_message~t100key-attr4.
      ELSE.
        MESSAGE 'Rest client Exception' TYPE 'E'.
      ENDIF.
  ENDTRY.

  zlo_http_client->close( ).

Security Services Tools

SAP offers on GitHub some extra Security Service Tools. These are custom Z ABAPs you can download and modify to your needs.

Link to GitHub:

GitHub – SAP-samples/security-services-tools: If you use security-related services and tools such as EWA, SOS, System Recommendations, Configuration Validation, or a security dashboard in SAP Solution Manager, the ABAP reports in this repository can help with further analysis and development.

Interesting programs from Security Service Tools

Some highlights from the Security Service Tools page:

  • Extensive cleanup program for weak hashes (including the password history data)
  • Workload statistics of RFC calls
  • Show SNC status of active users on application server
  • Show RFC gateway and logging settings
  • History of dynamic profile parameters
  • ….
  • Many more

SICF tips and trikcs

SICF is an abbreviation for SAP internet communication framework.

It is used to expose internet services like SAP ABAP webdynpro, ODATA etc.

Checking active services

As per SAP “Note 1555208 – ICF services become inactive after upgrade or SP update” you can find the list of active services with the report RS_ICF_SERV_ADMIN_TASKS (choose option Export of Active Services into CSV file).

On table level: Check the table ICFSERVLOC. All active services are marked with an “X” flag.

Checking SICF security settings

Don’t use the old program RSICFCHK (see OSS note 3300857 – Report RSICFCHK shows incomplete result). Use the new SECSTORE transaction. At the start of transaction SECSTORE choose in the check entries section “ICF Service”:

Now hit execute and check the results:

Mass processing

SICF mass processing is done via program RS_ICF_SERV_MASS_PROCESSING.

Logging of SICF changes

To enable logging of SICF changes: switch on table logging for table ICFSERVLOC.

Various OSS notes around SICF

User based debugging

In some cases you need to debug the session of another user. This can be needed for example when you need to solve an issue in ABAP for a FIORI app. The end user is doing his work until the break point is reached. Then you take over the session using the normal debugging tools. The basic principle is explained in OSS note 1919888 – Debugging the applications of another user, and in this SAP help file.

Prerequisites:

Then let the user start the work. You will take over as soon as the break point is reached.

Checklist for issues can be found in OSS note 2462481 – External debug / breakpoint is not recognized.

Set the user ID to be debugged

For your user ID choose the menu option Utilities/Settings. Then select main tab ABAP editor and subtab Debugging:

Now replace your user with the user name for which you want to take the session over using the external break point.

Custom SCI class for checking AUTHORITY-CHECK statement

Follow the steps explained in this blog to set up a new custom check. We will use these steps to set up an extra SCI class to check if the AUTHORITY-CHECK statement is added in ABAP code or not.

New SCI check coding

Step 1: create the ZCL_CI_SCAN_AUTH class

In SE24 copy class CL_CI_TEST_FREE_SEARCH to ZCL_CI_SCAN_AUTH. In the attributes of the copied class set C_MY_NAME as variable to ‘ZCL_CI_SCAN_AUTH’. Also set the error code MCODE_0001 to ‘Z001’.

Step 2: redo the CONSTRUCTOR

Goto the constructor of the new class and overwrite the existing code with this code snippit:

 super->constructor).
 
   description    'Search authority check statement'(001).  "required
   category       'ZCL_OWN_CHECKS'.         "required
   version        '000'.                      "required
   has_attributes c_false.                        "optional
   attributes_ok  c_false.                 "optional
   DEFINE fill_message.
     CLEAR smsg.
     smsg-test c_my_name.
     smsg-code &1.  "message code
     smsg-kind &2.  "message priority
     smsg-text &3.  "message text
     smsg-pcom &4.  "pseudocomment
     INSERT smsg INTO TABLE scimessages.
   END-OF-DEFINITION.
 
   fill_message 'Z001' 'E' 'Search authority check statement'(001' '.

Don’t forget to double click on the 001 to generate the text message.

Step 3: adapt the RUN code

Now the check itself has to be built in the RUN method:

DATA:
     l_include       TYPE sobj_name,
     l_row           TYPE token_row,
     l_column        TYPE token_col,
     l_tokennr       LIKE statement_wa-from,
     l_code          TYPE sci_errc,
     l_search_string LIKE LINE OF search_strings VALUE 'AUTHORITY-CHECK',
     l_position      TYPE i,
     l_found         TYPE VALUE ' '.
 
 *  IF search_strings IS INITIAL.
 *    RETURN.
 *  ENDIF.
 
   IF ref_scan IS INITIAL.
     CHECK get'X'.
   ENDIF.
 
   CHECK ref_scan->subrc 0.
 
 *-- loop at all tokens
   LOOP AT ref_scan->statements INTO statement_wa.
     CHECK statement_wa-from <= statement_wa-to.
     l_position sy-tabix.
     IF statement_wa-type 'S' OR
        statement_wa-type 'P'.
       CHECK comment_mode 'X'.
     ENDIF.
 
     LOOP AT ref_scan->tokens INTO token_wa
            FROM statement_wa-from TO statement_wa-to.
       l_tokennr sy-tabix.
       IF token_wa-type 'S'.
         CHECK literal_mode 'X'.
       ENDIF.
 
 *-- does ABAP-string contain search-string ?
       IF token_wa-str CP l_search_string.
         UNPACK sy-tabix TO l_code(4).
         l_include get_include).
 
         l_row     get_line_absl_tokennr ).
         l_column  get_column_absl_tokennr ).
         l_found 'X'.
 
         EXIT.
       ENDIF.     "l_strpos > l_pos
 
     ENDLOOP.
   ENDLOOP.
   IF l_found NE 'X'.
     informp_sub_obj_type c_type_include
                 p_sub_obj_name l_include
                 p_position     l_position
                 p_line         l_row
                 p_column       l_column
                 p_kind         'E'
                 p_test         c_my_name
                 p_code         'Z001'
                 p_suppress     '"#EC CI_NOAUTH '
                 p_param_1      token_wa-str ).
   ENDIF.

Basically the code looks for the statement ‘AUTHORITHY-CHECK’. If it found nothing happens. If it is found, it will generate a message.

Step 4: generating the message

In the method GET_MESSAGE_TEXT overwrite the code with this new code:

   data:
     L_CODE type SCI_ERRC.
 
   if P_TEST <> MYNAME or p_code c_code_not_remote_enabled.
     SUPER->GET_MESSAGE_TEXT(
                 exporting P_TEST P_TEST P_CODE P_CODE
                 importing P_TEXT P_TEXT ).
     return.
   endif.
   L_CODE P_CODE.
   shift L_CODE left deleting leading SPACE.
   P_TEXT 'No authorithy-check statement found'(101).
   replace first occurrence of '&N' in P_TEXT with L_CODE.

SCI settings

Use steps from blog xxx to add the new check to the SCI variant ZTEST.

SCI variant

Test program

We have written a simple test program without AUTHORITHY-CHECK.

Test program ZTEST

When running the SCI with our test variant, this is the result:

SCI check on AUTHORITHY-CHECK statement

 

EWA alert solutions

The SAP Early Watch Alert (EWA) report can contain valuable information on specific topics in your system that you must fix.

More information on the EWA workspace can be found in this blog.

General EWA tips and tricks can be found in this blog.

EWA alert solution finder

On the me.sap.com go to the specific page for EWA solution finder:

Action follow up is on the right hand side per alert:

You can choose:

  • Hide the alert (be very careful!)
  • Snooze the alert (see SAP blog)
  • Mail the alert

The snoozing is a very useful function. For example, you detect a red alert on HANA revision, but it will take you a month to implement (due to regression testing, process, etc), you can now snooze the alert for the implementation period:

After the implementation is done, the alert should be gone. If the implementation is delayed, you will be reminded again.

ABAPDOCU: online ABAP documentation

All ABAP documentation is online available in your ABAP system. Start transaction ABAPDOCU:

The most used function is the traditional ABAP keyword documentation.

But the ABAPDOCU is constantly updated and also contains great background information on:

  • CDS views
  • Restful Application Programming model (RAP)
  • Security in ABAP
  • Cloud rules for ABAP
  • Release notes for ABAP releases
  • ABAP examples
  • Tools for ABAP programming

OSS notes

Set up AIF interface error handling

SAP has a nice framework to handle interface errors: AIF (application integration framework).

The framework is quite old already but was not very popular. Not to the fact that it is not good (it is in fact quite good), but it is because it is a licensed product and too expensive for the value it brings.

The framework is now split into 2 parts:

  • Use in combination with standard SAP scenarios (in this case the use of AIF is free)
  • Use in combination with your own custom flows (in this case you need to pay for the AIF license)

Activating AIF error handling content

For reference you can read the formal help file from SAP on activating AIF content.

Extract the content using SE38 report /AIF/CONTENT_EXTRACT.

Then start transaction /AIF/CUST to maintain the customizing for errors. The input is given on this SAP help page.


And define the error handling levels:

Use of AIF

Transaction /AIF/ERR can be used for the error handling monitoring: