mirror of
https://github.com/abapGit/abapGit.git
synced 2025-05-01 20:32:26 +08:00
590 lines
22 KiB
ABAP
590 lines
22 KiB
ABAP
*&---------------------------------------------------------------------*
|
|
*& Include ZABAPGIT_2FA
|
|
*&---------------------------------------------------------------------*
|
|
|
|
"! Exception base class for two factor authentication related errors
|
|
CLASS lcx_2fa_error DEFINITION INHERITING FROM cx_static_check.
|
|
PUBLIC SECTION.
|
|
METHODS:
|
|
constructor IMPORTING is_textid LIKE textid OPTIONAL
|
|
ix_previous LIKE previous OPTIONAL
|
|
iv_error_text TYPE csequence OPTIONAL,
|
|
get_text REDEFINITION.
|
|
DATA:
|
|
mv_text TYPE string READ-ONLY.
|
|
PROTECTED SECTION.
|
|
METHODS:
|
|
get_default_text RETURNING VALUE(rv_text) TYPE string.
|
|
ENDCLASS.
|
|
|
|
CLASS lcx_2fa_error IMPLEMENTATION.
|
|
METHOD constructor.
|
|
super->constructor( textid = is_textid previous = ix_previous ).
|
|
mv_text = iv_error_text.
|
|
ENDMETHOD.
|
|
|
|
METHOD get_text.
|
|
IF mv_text IS NOT INITIAL.
|
|
result = mv_text.
|
|
ELSEIF get_default_text( ) IS NOT INITIAL.
|
|
result = get_default_text( ).
|
|
ELSE.
|
|
result = super->get_text( ).
|
|
ENDIF.
|
|
ENDMETHOD.
|
|
|
|
METHOD get_default_text.
|
|
rv_text = 'Error in two factor authentication.' ##NO_TEXT.
|
|
ENDMETHOD.
|
|
ENDCLASS.
|
|
|
|
CLASS lcx_2fa_auth_failed DEFINITION INHERITING FROM lcx_2fa_error FINAL.
|
|
PROTECTED SECTION.
|
|
METHODS:
|
|
get_default_text REDEFINITION.
|
|
ENDCLASS.
|
|
|
|
CLASS lcx_2fa_auth_failed IMPLEMENTATION.
|
|
METHOD get_default_text.
|
|
rv_text = 'Authentication failed using 2FA.' ##NO_TEXT.
|
|
ENDMETHOD.
|
|
ENDCLASS.
|
|
|
|
CLASS lcx_2fa_token_gen_failed DEFINITION INHERITING FROM lcx_2fa_error FINAL.
|
|
PROTECTED SECTION.
|
|
METHODS:
|
|
get_default_text REDEFINITION.
|
|
ENDCLASS.
|
|
|
|
CLASS lcx_2fa_token_gen_failed IMPLEMENTATION.
|
|
METHOD get_default_text.
|
|
rv_text = 'Two factor access token generation failed.' ##NO_TEXT.
|
|
ENDMETHOD.
|
|
ENDCLASS.
|
|
|
|
CLASS lcx_2fa_unsupported DEFINITION INHERITING FROM lcx_2fa_error FINAL.
|
|
PROTECTED SECTION.
|
|
METHODS:
|
|
get_default_text REDEFINITION.
|
|
ENDCLASS.
|
|
|
|
CLASS lcx_2fa_unsupported IMPLEMENTATION.
|
|
METHOD get_default_text.
|
|
rv_text = 'The service is not supported for two factor authentication.' ##NO_TEXT.
|
|
ENDMETHOD.
|
|
ENDCLASS.
|
|
|
|
CLASS lcx_2fa_no_cached_token DEFINITION INHERITING FROM lcx_2fa_error FINAL.
|
|
PROTECTED SECTION.
|
|
METHODS:
|
|
get_default_text REDEFINITION.
|
|
ENDCLASS.
|
|
|
|
CLASS lcx_2fa_no_cached_token IMPLEMENTATION.
|
|
METHOD get_default_text.
|
|
rv_text = 'Cached two factor access token requested but not available.' ##NO_TEXT.
|
|
ENDMETHOD.
|
|
ENDCLASS.
|
|
|
|
CLASS lcx_2fa_cache_deletion_failed DEFINITION INHERITING FROM lcx_2fa_error FINAL.
|
|
PROTECTED SECTION.
|
|
METHODS:
|
|
get_default_text REDEFINITION.
|
|
ENDCLASS.
|
|
|
|
CLASS lcx_2fa_cache_deletion_failed IMPLEMENTATION.
|
|
METHOD get_default_text.
|
|
rv_text = 'Cache deletion failed.' ##NO_TEXT.
|
|
ENDMETHOD.
|
|
ENDCLASS.
|
|
|
|
"! Defines a two factor authentication authenticator
|
|
"! <p>
|
|
"! Authenticators support one or multiple services and are able to generate access tokens using the
|
|
"! service's API using the users username, password and two factor authentication token
|
|
"! (app/sms/tokengenerator). With these access tokens the user can be authenticated to the service's
|
|
"! implementation of the git http api, just like the "normal" password would. The authenticator can
|
|
"! also store and retrieve the access token it generated.
|
|
"! </p>
|
|
"! <p>
|
|
"! <em>LCL_2FA_AUTHENTICATOR_REGISTRY</em> can be used to find a suitable implementation for a given
|
|
"! repository.
|
|
"! </p>
|
|
INTERFACE lif_2fa_authenticator.
|
|
METHODS:
|
|
"! Generate an access token
|
|
"! @parameter iv_url | Repository url
|
|
"! @parameter iv_username | Username
|
|
"! @parameter iv_password | Password
|
|
"! @parameter iv_2fa_token | Two factor token
|
|
"! @parameter rv_access_token | Generated access token
|
|
"! @raising lcx_2fa_auth_failed | Authentication failed
|
|
"! @raising lcx_2fa_token_gen_failed | Token generation failed
|
|
authenticate IMPORTING iv_url TYPE string
|
|
iv_username TYPE string
|
|
iv_password TYPE string
|
|
iv_2fa_token TYPE string
|
|
RETURNING VALUE(rv_access_token) TYPE string
|
|
RAISING lcx_2fa_auth_failed
|
|
lcx_2fa_token_gen_failed,
|
|
"! Check if this authenticator instance supports the give repository url
|
|
"! @parameter iv_url | Repository url
|
|
"! @parameter rv_supported | Is supported
|
|
supports_url IMPORTING iv_url TYPE string
|
|
RETURNING VALUE(rv_supported) TYPE abap_bool,
|
|
"! Get a unique identifier for the service that hosts the repository
|
|
"! @parameter iv_url | Repository url
|
|
"! @parameter rv_id | Service id
|
|
get_service_id_from_url IMPORTING iv_url TYPE string
|
|
RETURNING VALUE(rv_id) TYPE string,
|
|
"! Check if there is a cached access token (for the current user)
|
|
"! @parameter iv_url | Repository url
|
|
"! @parameter rv_available | Token is cached
|
|
is_cached_access_token_avail IMPORTING iv_url TYPE string
|
|
RETURNING VALUE(rv_available) TYPE abap_bool,
|
|
"! Get a cached access token
|
|
"! <p>
|
|
"! Username and password are also parameters to decrypt the token if needed. They must no
|
|
"! necessarily be provided if the used authenticator does not use encryption.
|
|
"! </p>
|
|
"! @parameter iv_url | Repository url
|
|
"! @parameter iv_username | Username
|
|
"! @parameter iv_password | Password
|
|
"! @parameter rv_token | Access token
|
|
"! @raising lcx_2fa_no_cached_token | There is no cached token
|
|
get_cached_access_token IMPORTING iv_url TYPE string
|
|
iv_username TYPE string OPTIONAL
|
|
iv_password TYPE string OPTIONAL
|
|
RETURNING VALUE(rv_token) TYPE string
|
|
RAISING lcx_2fa_no_cached_token,
|
|
"! Delete a cached token
|
|
"! @parameter iv_url | Repository url
|
|
"! @raising lcx_2fa_cache_deletion_failed | Deletion failed
|
|
delete_cached_access_token IMPORTING iv_url TYPE string
|
|
RAISING lcx_2fa_cache_deletion_failed.
|
|
ENDINTERFACE.
|
|
|
|
"! Default <em>LIF_2FA-AUTHENTICATOR</em> implememtation
|
|
"! <p>
|
|
"! This uses the user settings to store cached access tokens and encrypts / decrypts them as needed.
|
|
"! </p>
|
|
CLASS lcl_2fa_authenticator_base DEFINITION
|
|
ABSTRACT
|
|
CREATE PUBLIC.
|
|
|
|
PUBLIC SECTION.
|
|
INTERFACES:
|
|
lif_2fa_authenticator.
|
|
ALIASES:
|
|
authenticate FOR lif_2fa_authenticator~authenticate,
|
|
supports_url FOR lif_2fa_authenticator~supports_url,
|
|
get_service_id_from_url FOR lif_2fa_authenticator~get_service_id_from_url,
|
|
is_cached_access_token_avail FOR lif_2fa_authenticator~is_cached_access_token_avail,
|
|
get_cached_access_token FOR lif_2fa_authenticator~get_cached_access_token,
|
|
delete_cached_token FOR lif_2fa_authenticator~delete_cached_access_token.
|
|
METHODS:
|
|
"! @parameter iv_supported_url_regex | Regular expression to check if a repository url is
|
|
"! supported, used for default implementation of
|
|
"! <em>SUPPORTS_URL</em>
|
|
constructor IMPORTING iv_supported_url_regex TYPE clike.
|
|
PROTECTED SECTION.
|
|
METHODS:
|
|
"! Subclass implementation of <em>LIF_2FA_AUTHENTICATOR=>AUTHENTICATE</em>
|
|
"! <p>
|
|
"! The caller will take care of caching the token.
|
|
"! </p>
|
|
"! @parameter iv_url | Repository url
|
|
"! @parameter iv_username | Username
|
|
"! @parameter iv_password | Password
|
|
"! @parameter iv_2fa_token | Two factor token
|
|
"! @parameter rv_access_token | Generated access token
|
|
"! @raising lcx_2fa_auth_failed | Authentication failed
|
|
"! @raising lcx_2fa_token_gen_failed | Token generation failed
|
|
authenticate_internal ABSTRACT IMPORTING iv_url TYPE string
|
|
iv_username TYPE string
|
|
iv_password TYPE string
|
|
iv_2fa_token TYPE string
|
|
RETURNING VALUE(rv_access_token) TYPE string
|
|
RAISING lcx_2fa_auth_failed
|
|
lcx_2fa_token_gen_failed,
|
|
"! Helper method to raise class based exception after traditional exception was raised
|
|
"! <p>
|
|
"! <em>sy-msg...</em> must be set right before calling!
|
|
"! </p>
|
|
raise_internal_error_from_sy FINAL RAISING lcx_2fa_auth_failed.
|
|
PRIVATE SECTION.
|
|
DATA:
|
|
mo_url_regex TYPE REF TO cl_abap_regex.
|
|
ENDCLASS.
|
|
|
|
CLASS lcl_2fa_authenticator_base IMPLEMENTATION.
|
|
METHOD constructor.
|
|
CREATE OBJECT mo_url_regex
|
|
EXPORTING
|
|
pattern = iv_supported_url_regex
|
|
ignore_case = abap_true.
|
|
ENDMETHOD.
|
|
|
|
METHOD authenticate.
|
|
DATA: lv_encrypted_token TYPE string.
|
|
|
|
rv_access_token = authenticate_internal( iv_url = iv_url
|
|
iv_username = iv_username
|
|
iv_password = iv_password
|
|
iv_2fa_token = iv_2fa_token ).
|
|
|
|
" Store the access token, by default in the user settings
|
|
|
|
" 1. Encrypt it
|
|
* lv_encrypted_token = cl_encryption_helper=>encrypt_symmetric( iv_text = rv_access_token
|
|
* iv_key = iv_password ).
|
|
" TODO: Find something like the above for symmetric encryption
|
|
lv_encrypted_token = rv_access_token.
|
|
|
|
" 2. Store it
|
|
TRY.
|
|
lcl_app=>user( )->set_2fa_access_token( iv_service_id = get_service_id_from_url( iv_url )
|
|
iv_username = iv_username
|
|
iv_token = lv_encrypted_token ).
|
|
CATCH lcx_exception ##NO_HANDLER.
|
|
" Not the biggest of deals if caching the token fails
|
|
ENDTRY.
|
|
ENDMETHOD.
|
|
|
|
METHOD supports_url.
|
|
rv_supported = mo_url_regex->create_matcher( text = iv_url )->match( ).
|
|
ENDMETHOD.
|
|
|
|
METHOD get_service_id_from_url.
|
|
rv_id = 'UNKNOWN SERVICE'. " Please overwrite in subclasses
|
|
ENDMETHOD.
|
|
|
|
METHOD is_cached_access_token_avail.
|
|
DATA: lv_service_id TYPE string.
|
|
|
|
lv_service_id = get_service_id_from_url( iv_url ).
|
|
|
|
" Default storage location is user settings
|
|
TRY.
|
|
rv_available = boolc( lcl_app=>user( )->get_2fa_access_token( lv_service_id )
|
|
IS NOT INITIAL ).
|
|
CATCH lcx_exception.
|
|
rv_available = abap_false.
|
|
ENDTRY.
|
|
ENDMETHOD.
|
|
|
|
METHOD get_cached_access_token.
|
|
DATA: lv_access_token_encrypted TYPE string,
|
|
lx_error TYPE REF TO cx_root.
|
|
|
|
TRY.
|
|
lv_access_token_encrypted
|
|
= lcl_app=>user( )->get_2fa_access_token( get_service_id_from_url( iv_url ) ).
|
|
CATCH lcx_exception INTO lx_error.
|
|
RAISE EXCEPTION TYPE lcx_2fa_no_cached_token
|
|
EXPORTING
|
|
ix_previous = lx_error
|
|
iv_error_text = lx_error->get_text( ).
|
|
ENDTRY.
|
|
|
|
IF lv_access_token_encrypted IS INITIAL.
|
|
RAISE EXCEPTION TYPE lcx_2fa_no_cached_token.
|
|
ENDIF.
|
|
|
|
" TODO: Decryption
|
|
* rv_token = cl_encryption_helper=>decrypt_symmetric( iv_encrypted = rv_access_token
|
|
* iv_key = iv_password ).
|
|
rv_token = lv_access_token_encrypted.
|
|
ENDMETHOD.
|
|
|
|
METHOD delete_cached_token.
|
|
DATA: lx_ex TYPE REF TO cx_root.
|
|
|
|
TRY.
|
|
" Default storage location is user settings
|
|
lcl_app=>user( )->delete_2fa_config( get_service_id_from_url( iv_url ) ).
|
|
CATCH lcx_exception INTO lx_ex.
|
|
RAISE EXCEPTION TYPE lcx_2fa_cache_deletion_failed
|
|
EXPORTING
|
|
ix_previous = lx_ex
|
|
iv_error_text = |Cache deletion failed: { lx_ex->get_text( ) }|.
|
|
ENDTRY.
|
|
ENDMETHOD.
|
|
|
|
METHOD raise_internal_error_from_sy.
|
|
DATA: lv_error_msg TYPE string.
|
|
|
|
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
|
|
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
|
|
INTO lv_error_msg.
|
|
RAISE EXCEPTION TYPE lcx_2fa_auth_failed
|
|
EXPORTING
|
|
iv_error_text = |Internal error: { lv_error_msg }| ##NO_TEXT.
|
|
ENDMETHOD.
|
|
ENDCLASS.
|
|
|
|
CLASS lcl_2fa_github_authenticator DEFINITION
|
|
INHERITING FROM lcl_2fa_authenticator_base
|
|
FINAL
|
|
CREATE PUBLIC.
|
|
|
|
PUBLIC SECTION.
|
|
METHODS:
|
|
constructor,
|
|
get_service_id_from_url REDEFINITION.
|
|
PROTECTED SECTION.
|
|
METHODS:
|
|
authenticate_internal REDEFINITION.
|
|
PRIVATE SECTION.
|
|
METHODS:
|
|
set_access_token_request IMPORTING ii_request TYPE REF TO if_http_request
|
|
iv_repo_name TYPE string,
|
|
get_token_from_response IMPORTING ii_response TYPE REF TO if_http_response
|
|
RETURNING VALUE(rv_token) TYPE string,
|
|
parse_repo_from_url IMPORTING iv_url TYPE string
|
|
RETURNING VALUE(rv_repo_name) TYPE string.
|
|
ENDCLASS.
|
|
|
|
CLASS lcl_2fa_github_authenticator IMPLEMENTATION.
|
|
METHOD constructor.
|
|
super->constructor( 'https?:\/\/(www\.)?github.com.*$' ).
|
|
ENDMETHOD.
|
|
|
|
METHOD authenticate_internal.
|
|
CONSTANTS: lc_github_api_url TYPE string VALUE `https://api.github.com/`,
|
|
lc_otp_header_name TYPE string VALUE `X-Github-OTP`,
|
|
lc_restendpoint_authorizations TYPE string VALUE `/authorizations`.
|
|
DATA: li_http_client TYPE REF TO if_http_client,
|
|
lv_http_code TYPE i,
|
|
lv_http_code_description TYPE string,
|
|
lv_binary_response TYPE xstring,
|
|
BEGIN OF ls_success_response,
|
|
token TYPE string,
|
|
END OF ls_success_response.
|
|
|
|
" 1. Try to login to GitHub API with username, password and 2fa token
|
|
cl_http_client=>create_by_url(
|
|
EXPORTING
|
|
url = lc_github_api_url
|
|
IMPORTING
|
|
client = li_http_client
|
|
EXCEPTIONS
|
|
argument_not_found = 1
|
|
plugin_not_active = 2
|
|
internal_error = 3
|
|
OTHERS = 4 ).
|
|
IF sy-subrc <> 0.
|
|
raise_internal_error_from_sy( ).
|
|
ENDIF.
|
|
|
|
" https://developer.github.com/v3/auth/#working-with-two-factor-authentication
|
|
li_http_client->propertytype_accept_cookie = if_http_client=>co_enabled.
|
|
li_http_client->request->set_header_field( name = lc_otp_header_name value = iv_2fa_token ).
|
|
li_http_client->authenticate( username = iv_username password = iv_password ).
|
|
li_http_client->propertytype_logon_popup = if_http_client=>co_disabled.
|
|
|
|
li_http_client->send( EXCEPTIONS OTHERS = 1 ).
|
|
IF sy-subrc <> 0.
|
|
raise_internal_error_from_sy( ).
|
|
ENDIF.
|
|
|
|
li_http_client->receive( EXCEPTIONS OTHERS = 1 ).
|
|
IF sy-subrc <> 0.
|
|
raise_internal_error_from_sy( ).
|
|
ENDIF.
|
|
|
|
" Check if authentication has succeeded
|
|
li_http_client->response->get_status(
|
|
IMPORTING
|
|
code = lv_http_code
|
|
reason = lv_http_code_description ).
|
|
IF lv_http_code <> 200.
|
|
RAISE EXCEPTION TYPE lcx_2fa_auth_failed
|
|
EXPORTING
|
|
iv_error_text = |Authentication failed: { lv_http_code_description }|.
|
|
ENDIF.
|
|
|
|
|
|
" 2. Create an access token which can be used instead of a password
|
|
" https://developer.github.com/v3/oauth_authorizations/#create-a-new-authorization
|
|
|
|
set_access_token_request( ii_request = li_http_client->request
|
|
iv_repo_name = parse_repo_from_url( iv_url ) ).
|
|
li_http_client->request->set_header_field( name = if_http_header_fields_sap=>request_uri
|
|
value = lc_restendpoint_authorizations ).
|
|
li_http_client->request->set_method( if_http_request=>co_request_method_post ).
|
|
|
|
li_http_client->send( EXCEPTIONS OTHERS = 1 ).
|
|
IF sy-subrc <> 0.
|
|
raise_internal_error_from_sy( ).
|
|
ENDIF.
|
|
|
|
li_http_client->receive( EXCEPTIONS OTHERS = 1 ).
|
|
IF sy-subrc <> 0.
|
|
raise_internal_error_from_sy( ).
|
|
ENDIF.
|
|
|
|
li_http_client->response->get_status(
|
|
IMPORTING
|
|
code = lv_http_code
|
|
reason = lv_http_code_description ).
|
|
IF lv_http_code <> 201.
|
|
RAISE EXCEPTION TYPE lcx_2fa_token_gen_failed
|
|
EXPORTING
|
|
iv_error_text = |Token generation failed: { lv_http_code } { lv_http_code_description }|.
|
|
ENDIF.
|
|
|
|
rv_access_token = get_token_from_response( li_http_client->response ).
|
|
IF rv_access_token IS INITIAL.
|
|
RAISE EXCEPTION TYPE lcx_2fa_token_gen_failed.
|
|
ENDIF.
|
|
ENDMETHOD.
|
|
|
|
METHOD set_access_token_request.
|
|
CONSTANTS: BEGIN OF lc_create_access_token_request,
|
|
scopes TYPE string VALUE 'repo',
|
|
note TYPE string VALUE 'abapGit',
|
|
END OF lc_create_access_token_request.
|
|
DATA: lo_json_writer TYPE REF TO cl_sxml_string_writer,
|
|
lt_scopes TYPE stringtab,
|
|
lt_rest_parvalues TYPE abap_trans_srcbind_tab,
|
|
ls_rest_line LIKE LINE OF lt_rest_parvalues,
|
|
lt_result_parvalues TYPE abap_trans_resbind_tab,
|
|
ls_result_line LIKE LINE OF lt_result_parvalues,
|
|
lr_data_ref TYPE REF TO data,
|
|
lv_note TYPE string,
|
|
lv_fingerprint TYPE string.
|
|
|
|
lo_json_writer = cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ).
|
|
APPEND lc_create_access_token_request-scopes TO lt_scopes.
|
|
|
|
GET REFERENCE OF lc_create_access_token_request-scopes INTO lr_data_ref.
|
|
ls_rest_line-name = 'scopes'.
|
|
ls_rest_line-value = lr_data_ref.
|
|
APPEND ls_rest_line TO lt_rest_parvalues.
|
|
|
|
GET REFERENCE OF lc_create_access_token_request-note INTO lr_data_ref.
|
|
ls_rest_line-name = 'note'.
|
|
ls_rest_line-value = lr_data_ref.
|
|
APPEND ls_rest_line TO lt_rest_parvalues.
|
|
|
|
" The fingerprint must be unique, otherwise only one token can be generated, unless the user
|
|
" deletes it in GitHub's settings. This is problematic if he deletes it in abapGit but keeps it
|
|
" on GitHub.
|
|
lv_fingerprint = |abapGit-{ sy-sysid }-{ sy-uname }-{ sy-datum }-{ sy-uzeit }|.
|
|
GET REFERENCE OF lv_fingerprint INTO lr_data_ref.
|
|
ls_rest_line-name = 'fingerprint'.
|
|
ls_rest_line-value = lr_data_ref.
|
|
APPEND ls_rest_line TO lt_rest_parvalues.
|
|
|
|
" Dynamic source table is used because otherwise identifiers will always be written in uppercase
|
|
" which is not supported by GitHub's API.
|
|
CALL TRANSFORMATION id SOURCE (lt_rest_parvalues)
|
|
RESULT XML lo_json_writer.
|
|
|
|
ii_request->set_data( lo_json_writer->get_output( ) ).
|
|
ENDMETHOD.
|
|
|
|
METHOD get_token_from_response.
|
|
CONSTANTS: lc_token_field_name TYPE string VALUE 'token'.
|
|
DATA: lt_result_parvalues TYPE abap_trans_resbind_tab,
|
|
ls_result_line LIKE LINE OF lt_result_parvalues,
|
|
lr_data_ref TYPE REF TO data,
|
|
lv_binary_response TYPE xstring.
|
|
|
|
GET REFERENCE OF rv_token INTO lr_data_ref.
|
|
ls_result_line-name = lc_token_field_name.
|
|
ls_result_line-value = lr_data_ref.
|
|
APPEND ls_result_line TO lt_result_parvalues.
|
|
|
|
lv_binary_response = ii_response->get_data( ).
|
|
|
|
CALL TRANSFORMATION id SOURCE XML lv_binary_response
|
|
RESULT (lt_result_parvalues).
|
|
ENDMETHOD.
|
|
|
|
METHOD parse_repo_from_url.
|
|
DATA: lo_regex TYPE REF TO cl_abap_regex,
|
|
lo_matcher TYPE REF TO cl_abap_matcher.
|
|
|
|
CREATE OBJECT lo_regex
|
|
EXPORTING
|
|
pattern = 'https?:\/\/(www\.)?github.com\/(.*)$'.
|
|
|
|
lo_matcher = lo_regex->create_matcher( text = iv_url ).
|
|
IF lo_matcher->match( ) = abap_true.
|
|
rv_repo_name = lo_matcher->get_submatch( 1 ).
|
|
ELSE.
|
|
rv_repo_name = '???' ##NO_TEXT.
|
|
ENDIF.
|
|
ENDMETHOD.
|
|
|
|
METHOD get_service_id_from_url.
|
|
rv_id = 'github'.
|
|
ENDMETHOD.
|
|
ENDCLASS.
|
|
|
|
"! Static registry class to find <em>LIF_2FA_AUTHENTICATOR</em> instances
|
|
CLASS lcl_2fa_authenticator_registry DEFINITION
|
|
FINAL
|
|
CREATE PRIVATE.
|
|
|
|
PUBLIC SECTION.
|
|
CLASS-METHODS:
|
|
class_constructor,
|
|
"! Retrieve an authenticator instance by url
|
|
"! @parameter iv_url | Url of the repository / service
|
|
"! @parameter ro_authenticator | Found authenticator instance
|
|
"! @raising lcx_2fa_unsupported | No authenticator found that supports the service
|
|
get_authenticator_for_url IMPORTING iv_url TYPE string
|
|
RETURNING VALUE(ro_authenticator) TYPE REF TO lif_2fa_authenticator
|
|
RAISING lcx_2fa_unsupported,
|
|
"! Check if there is a two factor authenticator available for the url
|
|
"! @parameter iv_url | Url of the repository / service
|
|
"! @parameter rv_supported | 2FA is supported
|
|
is_url_supported IMPORTING iv_url TYPE string
|
|
RETURNING VALUE(rv_supported) TYPE abap_bool.
|
|
CLASS-DATA:
|
|
"! All authenticators managed by the registry
|
|
gt_registered_authenticators TYPE HASHED TABLE OF REF TO lif_2fa_authenticator
|
|
WITH UNIQUE KEY table_line READ-ONLY.
|
|
PROTECTED SECTION.
|
|
PRIVATE SECTION.
|
|
ENDCLASS.
|
|
|
|
CLASS lcl_2fa_authenticator_registry IMPLEMENTATION.
|
|
METHOD class_constructor.
|
|
DEFINE register.
|
|
CREATE OBJECT li_authenticator TYPE &1.
|
|
INSERT li_authenticator INTO TABLE gt_registered_authenticators.
|
|
END-OF-DEFINITION.
|
|
|
|
DATA: li_authenticator TYPE REF TO lif_2fa_authenticator.
|
|
|
|
" If there are new authenticators these need to be added here manually.
|
|
" I do not think there is an equivalent to SEO_INTERFACE_IMPLEM_GET_ALL for local classes
|
|
" without invoking the compiler directly.
|
|
register: lcl_2fa_github_authenticator.
|
|
ENDMETHOD.
|
|
|
|
METHOD get_authenticator_for_url.
|
|
FIELD-SYMBOLS: <lo_authenticator> LIKE LINE OF gt_registered_authenticators.
|
|
|
|
LOOP AT gt_registered_authenticators ASSIGNING <lo_authenticator>.
|
|
IF <lo_authenticator>->supports_url( iv_url ) = abap_true.
|
|
ro_authenticator = <lo_authenticator>.
|
|
RETURN.
|
|
ENDIF.
|
|
ENDLOOP.
|
|
|
|
RAISE EXCEPTION TYPE lcx_2fa_unsupported.
|
|
ENDMETHOD.
|
|
|
|
METHOD is_url_supported.
|
|
TRY.
|
|
get_authenticator_for_url( iv_url ).
|
|
rv_supported = abap_true.
|
|
CATCH lcx_2fa_unsupported ##NO_HANDLER.
|
|
ENDTRY.
|
|
ENDMETHOD.
|
|
ENDCLASS. |