mirror of
https://github.com/abapGit/abapGit.git
synced 2025-04-30 20:03:20 +08:00
Lcl proxy auth (#1198)
* lcl_proxy_auth to global * lcx_2fa_error to global * 2FA to global
This commit is contained in:
parent
600b69b707
commit
540ebe16e1
110
src/http/zcl_abapgit_2fa_auth_base.clas.abap
Normal file
110
src/http/zcl_abapgit_2fa_auth_base.clas.abap
Normal file
|
@ -0,0 +1,110 @@
|
|||
"! Default <em>LIF_2FA-AUTHENTICATOR</em> implememtation
|
||||
CLASS zcl_abapgit_2fa_auth_base DEFINITION
|
||||
PUBLIC
|
||||
ABSTRACT
|
||||
CREATE PUBLIC .
|
||||
|
||||
PUBLIC SECTION.
|
||||
INTERFACES:
|
||||
zif_abapgit_2fa_authenticator.
|
||||
ALIASES:
|
||||
authenticate FOR zif_abapgit_2fa_authenticator~authenticate,
|
||||
supports_url FOR zif_abapgit_2fa_authenticator~supports_url,
|
||||
get_service_id_from_url FOR zif_abapgit_2fa_authenticator~get_service_id_from_url,
|
||||
is_2fa_required FOR zif_abapgit_2fa_authenticator~is_2fa_required,
|
||||
delete_access_tokens FOR zif_abapgit_2fa_authenticator~delete_access_tokens,
|
||||
begin FOR zif_abapgit_2fa_authenticator~begin,
|
||||
end FOR zif_abapgit_2fa_authenticator~end.
|
||||
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.
|
||||
CLASS-METHODS:
|
||||
"! 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_comm_error_from_sy RAISING zcx_abapgit_2fa_comm_error.
|
||||
METHODS:
|
||||
"! @parameter rv_running | Internal session is currently active
|
||||
is_session_running RETURNING VALUE(rv_running) TYPE abap_bool.
|
||||
PRIVATE SECTION.
|
||||
DATA:
|
||||
mo_url_regex TYPE REF TO cl_abap_regex,
|
||||
mv_session_running TYPE abap_bool.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
|
||||
CLASS ZCL_ABAPGIT_2FA_AUTH_BASE IMPLEMENTATION.
|
||||
|
||||
|
||||
METHOD constructor.
|
||||
CREATE OBJECT mo_url_regex
|
||||
EXPORTING
|
||||
pattern = iv_supported_url_regex
|
||||
ignore_case = abap_true.
|
||||
ENDMETHOD.
|
||||
|
||||
|
||||
METHOD is_session_running.
|
||||
rv_running = mv_session_running.
|
||||
ENDMETHOD.
|
||||
|
||||
|
||||
METHOD raise_comm_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 zcx_abapgit_2fa_comm_error
|
||||
EXPORTING
|
||||
mv_text = |Communication error: { lv_error_msg }| ##NO_TEXT.
|
||||
ENDMETHOD.
|
||||
|
||||
|
||||
METHOD authenticate.
|
||||
RAISE EXCEPTION TYPE zcx_abapgit_2fa_auth_failed. " Needs to be overwritten in subclasses
|
||||
ENDMETHOD.
|
||||
|
||||
|
||||
METHOD begin.
|
||||
IF mv_session_running = abap_true.
|
||||
RAISE EXCEPTION TYPE zcx_abapgit_2fa_illegal_state.
|
||||
ENDIF.
|
||||
|
||||
mv_session_running = abap_true.
|
||||
ENDMETHOD.
|
||||
|
||||
|
||||
METHOD delete_access_tokens.
|
||||
RAISE EXCEPTION TYPE zcx_abapgit_2fa_del_failed. " Needs to be overwritten in subclasses
|
||||
ENDMETHOD.
|
||||
|
||||
|
||||
METHOD end.
|
||||
IF mv_session_running = abap_false.
|
||||
RAISE EXCEPTION TYPE zcx_abapgit_2fa_illegal_state.
|
||||
ENDIF.
|
||||
|
||||
mv_session_running = abap_false.
|
||||
ENDMETHOD.
|
||||
|
||||
|
||||
METHOD get_service_id_from_url.
|
||||
rv_id = 'UNKNOWN SERVICE'. " Please overwrite in subclasses
|
||||
ENDMETHOD.
|
||||
|
||||
|
||||
METHOD is_2fa_required.
|
||||
rv_required = abap_false.
|
||||
ENDMETHOD.
|
||||
|
||||
|
||||
METHOD supports_url.
|
||||
rv_supported = mo_url_regex->create_matcher( text = iv_url )->match( ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
19
src/http/zcl_abapgit_2fa_auth_base.clas.xml
Normal file
19
src/http/zcl_abapgit_2fa_auth_base.clas.xml
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<abapGit version="v1.0.0" serializer="LCL_OBJECT_CLAS" serializer_version="v1.0.0">
|
||||
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
|
||||
<asx:values>
|
||||
<VSEOCLASS>
|
||||
<CLSNAME>ZCL_ABAPGIT_2FA_AUTH_BASE</CLSNAME>
|
||||
<VERSION>1</VERSION>
|
||||
<LANGU>E</LANGU>
|
||||
<DESCRIPT>2FA base</DESCRIPT>
|
||||
<EXPOSURE>2</EXPOSURE>
|
||||
<STATE>1</STATE>
|
||||
<CLSABSTRCT>X</CLSABSTRCT>
|
||||
<CLSCCINCL>X</CLSCCINCL>
|
||||
<FIXPT>X</FIXPT>
|
||||
<UNICODE>X</UNICODE>
|
||||
</VSEOCLASS>
|
||||
</asx:values>
|
||||
</asx:abap>
|
||||
</abapGit>
|
179
src/http/zcl_abapgit_2fa_auth_registry.clas.abap
Normal file
179
src/http/zcl_abapgit_2fa_auth_registry.clas.abap
Normal file
|
@ -0,0 +1,179 @@
|
|||
"! Static registry class to find <em>LIF_2FA_AUTHENTICATOR</em> instances
|
||||
CLASS zcl_abapgit_2fa_auth_registry DEFINITION
|
||||
PUBLIC
|
||||
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 zif_abapgit_2fa_authenticator
|
||||
RAISING zcx_abapgit_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,
|
||||
"! Offer to use two factor authentication if supported and required
|
||||
"! <p>
|
||||
"! This uses GUI functionality to display a popup to request the user to enter a two factor
|
||||
"! token. Also an dummy authentication request might be used to find out if two factor
|
||||
"! authentication is required for the account.
|
||||
"! </p>
|
||||
"! @parameter iv_url | Url of the repository / service
|
||||
"! @parameter cv_username | Username
|
||||
"! @parameter cv_password | Password, will be replaced by an access token if two factor
|
||||
"! authentication succeeds
|
||||
"! @raising zcx_abapgit_exception | Error in two factor authentication
|
||||
use_2fa_if_required IMPORTING iv_url TYPE string
|
||||
CHANGING cv_username TYPE string
|
||||
cv_password TYPE string
|
||||
RAISING zcx_abapgit_exception.
|
||||
CLASS-DATA:
|
||||
"! All authenticators managed by the registry
|
||||
gt_registered_authenticators TYPE HASHED TABLE OF REF TO zif_abapgit_2fa_authenticator
|
||||
WITH UNIQUE KEY table_line READ-ONLY.
|
||||
PRIVATE SECTION.
|
||||
CLASS-METHODS:
|
||||
popup_token
|
||||
RETURNING VALUE(rv_token) TYPE string
|
||||
RAISING zcx_abapgit_exception.
|
||||
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
|
||||
CLASS ZCL_ABAPGIT_2FA_AUTH_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 zif_abapgit_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: zcl_abapgit_2fa_github_auth.
|
||||
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 zcx_abapgit_2fa_unsupported.
|
||||
ENDMETHOD.
|
||||
|
||||
|
||||
METHOD is_url_supported.
|
||||
TRY.
|
||||
get_authenticator_for_url( iv_url ).
|
||||
rv_supported = abap_true.
|
||||
CATCH zcx_abapgit_2fa_unsupported ##NO_HANDLER.
|
||||
ENDTRY.
|
||||
ENDMETHOD.
|
||||
|
||||
|
||||
METHOD popup_token.
|
||||
|
||||
DATA: lv_returncode TYPE c,
|
||||
lt_fields TYPE TABLE OF sval.
|
||||
|
||||
FIELD-SYMBOLS: <ls_field> LIKE LINE OF lt_fields.
|
||||
|
||||
|
||||
APPEND INITIAL LINE TO lt_fields ASSIGNING <ls_field>.
|
||||
<ls_field>-tabname = 'TADIR'.
|
||||
<ls_field>-fieldname = 'OBJ_NAME'.
|
||||
<ls_field>-fieldtext = 'Two factor auth. token'.
|
||||
|
||||
CALL FUNCTION 'POPUP_GET_VALUES'
|
||||
EXPORTING
|
||||
no_value_check = abap_true
|
||||
popup_title = 'Two factor auth. token'
|
||||
IMPORTING
|
||||
returncode = lv_returncode
|
||||
TABLES
|
||||
fields = lt_fields
|
||||
EXCEPTIONS
|
||||
error_in_fields = 1
|
||||
OTHERS = 2. "#EC NOTEXT
|
||||
IF sy-subrc <> 0.
|
||||
zcx_abapgit_exception=>raise( 'Error from POPUP_GET_VALUES' ).
|
||||
ENDIF.
|
||||
|
||||
IF lv_returncode = 'A'.
|
||||
zcx_abapgit_exception=>raise( 'Authentication cancelled' ).
|
||||
ENDIF.
|
||||
|
||||
READ TABLE lt_fields INDEX 1 ASSIGNING <ls_field>.
|
||||
ASSERT sy-subrc = 0.
|
||||
rv_token = <ls_field>-value.
|
||||
|
||||
ENDMETHOD.
|
||||
|
||||
|
||||
METHOD use_2fa_if_required.
|
||||
DATA: li_authenticator TYPE REF TO zif_abapgit_2fa_authenticator,
|
||||
lv_2fa_token TYPE string,
|
||||
lv_access_token TYPE string,
|
||||
lx_ex TYPE REF TO cx_root.
|
||||
|
||||
IF is_url_supported( iv_url ) = abap_false.
|
||||
RETURN.
|
||||
ENDIF.
|
||||
|
||||
TRY.
|
||||
li_authenticator = get_authenticator_for_url( iv_url ).
|
||||
li_authenticator->begin( ).
|
||||
|
||||
" Is two factor authentication required for this account?
|
||||
IF li_authenticator->is_2fa_required( iv_url = iv_url
|
||||
iv_username = cv_username
|
||||
iv_password = cv_password ) = abap_true.
|
||||
|
||||
lv_2fa_token = popup_token( ).
|
||||
|
||||
" Delete an old access token if it exists
|
||||
li_authenticator->delete_access_tokens( iv_url = iv_url
|
||||
iv_username = cv_username
|
||||
iv_password = cv_password
|
||||
iv_2fa_token = lv_2fa_token ).
|
||||
|
||||
" Get a new access token
|
||||
lv_access_token = li_authenticator->authenticate( iv_url = iv_url
|
||||
iv_username = cv_username
|
||||
iv_password = cv_password
|
||||
iv_2fa_token = lv_2fa_token ).
|
||||
|
||||
" Use the access token instead of the password
|
||||
cv_password = lv_access_token.
|
||||
ENDIF.
|
||||
|
||||
li_authenticator->end( ).
|
||||
|
||||
CATCH zcx_abapgit_2fa_error INTO lx_ex.
|
||||
TRY.
|
||||
li_authenticator->end( ).
|
||||
CATCH zcx_abapgit_2fa_illegal_state ##NO_HANDLER.
|
||||
ENDTRY.
|
||||
|
||||
zcx_abapgit_exception=>raise( |2FA error: { lx_ex->get_text( ) }| ).
|
||||
ENDTRY.
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
18
src/http/zcl_abapgit_2fa_auth_registry.clas.xml
Normal file
18
src/http/zcl_abapgit_2fa_auth_registry.clas.xml
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<abapGit version="v1.0.0" serializer="LCL_OBJECT_CLAS" serializer_version="v1.0.0">
|
||||
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
|
||||
<asx:values>
|
||||
<VSEOCLASS>
|
||||
<CLSNAME>ZCL_ABAPGIT_2FA_AUTH_REGISTRY</CLSNAME>
|
||||
<VERSION>1</VERSION>
|
||||
<LANGU>E</LANGU>
|
||||
<DESCRIPT>2FA registry</DESCRIPT>
|
||||
<STATE>1</STATE>
|
||||
<CLSFINAL>X</CLSFINAL>
|
||||
<CLSCCINCL>X</CLSCCINCL>
|
||||
<FIXPT>X</FIXPT>
|
||||
<UNICODE>X</UNICODE>
|
||||
</VSEOCLASS>
|
||||
</asx:values>
|
||||
</asx:abap>
|
||||
</abapGit>
|
361
src/http/zcl_abapgit_2fa_github_auth.clas.abap
Normal file
361
src/http/zcl_abapgit_2fa_github_auth.clas.abap
Normal file
|
@ -0,0 +1,361 @@
|
|||
CLASS zcl_abapgit_2fa_github_auth DEFINITION
|
||||
PUBLIC
|
||||
INHERITING FROM zcl_abapgit_2fa_auth_base
|
||||
FINAL
|
||||
CREATE PUBLIC .
|
||||
|
||||
PUBLIC SECTION.
|
||||
METHODS:
|
||||
constructor,
|
||||
get_service_id_from_url REDEFINITION,
|
||||
authenticate REDEFINITION,
|
||||
is_2fa_required REDEFINITION,
|
||||
delete_access_tokens REDEFINITION,
|
||||
end REDEFINITION.
|
||||
PROTECTED SECTION.
|
||||
PRIVATE SECTION.
|
||||
CONSTANTS:
|
||||
gc_github_api_url TYPE string VALUE `https://api.github.com/`,
|
||||
gc_otp_header_name TYPE string VALUE `X-Github-OTP`,
|
||||
gc_restendpoint_authorizations TYPE string VALUE `/authorizations`.
|
||||
CLASS-METHODS:
|
||||
set_new_token_request IMPORTING ii_request TYPE REF TO if_http_request,
|
||||
get_token_from_response IMPORTING ii_response TYPE REF TO if_http_response
|
||||
RETURNING VALUE(rv_token) TYPE string,
|
||||
set_list_token_request IMPORTING ii_request TYPE REF TO if_http_request,
|
||||
get_tobedel_tokens_from_resp IMPORTING ii_response TYPE REF TO if_http_response
|
||||
RETURNING VALUE(rt_ids) TYPE stringtab,
|
||||
set_del_token_request IMPORTING ii_request TYPE REF TO if_http_request
|
||||
iv_token_id TYPE string.
|
||||
METHODS:
|
||||
get_authenticated_client IMPORTING iv_username TYPE string
|
||||
iv_password TYPE string
|
||||
iv_2fa_token TYPE string
|
||||
RETURNING VALUE(ri_client) TYPE REF TO if_http_client
|
||||
RAISING zcx_abapgit_2fa_auth_failed
|
||||
zcx_abapgit_2fa_comm_error.
|
||||
DATA:
|
||||
mi_authenticated_session TYPE REF TO if_http_client.
|
||||
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
|
||||
CLASS ZCL_ABAPGIT_2FA_GITHUB_AUTH IMPLEMENTATION.
|
||||
|
||||
|
||||
METHOD constructor.
|
||||
super->constructor( '^https?://(www\.)?github.com.*$' ).
|
||||
ENDMETHOD.
|
||||
|
||||
|
||||
METHOD get_authenticated_client.
|
||||
DATA: lv_http_code TYPE i,
|
||||
lv_http_code_description TYPE string,
|
||||
lo_settings TYPE REF TO zcl_abapgit_settings.
|
||||
|
||||
" If there is a cached client return it instead
|
||||
IF is_session_running( ) = abap_true AND mi_authenticated_session IS BOUND.
|
||||
ri_client = mi_authenticated_session.
|
||||
RETURN.
|
||||
ENDIF.
|
||||
|
||||
" Try to login to GitHub API with username, password and 2fa token
|
||||
|
||||
lo_settings = zcl_abapgit_persist_settings=>get_instance( )->read( ).
|
||||
|
||||
cl_http_client=>create_by_url(
|
||||
EXPORTING
|
||||
url = gc_github_api_url
|
||||
ssl_id = 'ANONYM'
|
||||
proxy_host = lo_settings->get_proxy_url( )
|
||||
proxy_service = lo_settings->get_proxy_port( )
|
||||
IMPORTING
|
||||
client = ri_client
|
||||
EXCEPTIONS
|
||||
argument_not_found = 1
|
||||
plugin_not_active = 2
|
||||
internal_error = 3
|
||||
OTHERS = 4 ).
|
||||
IF sy-subrc <> 0.
|
||||
raise_comm_error_from_sy( ).
|
||||
ENDIF.
|
||||
|
||||
" https://developer.github.com/v3/auth/#working-with-two-factor-authentication
|
||||
ri_client->propertytype_accept_cookie = if_http_client=>co_enabled.
|
||||
ri_client->request->set_header_field( name = gc_otp_header_name value = iv_2fa_token ).
|
||||
ri_client->authenticate( username = iv_username password = iv_password ).
|
||||
ri_client->propertytype_logon_popup = if_http_client=>co_disabled.
|
||||
|
||||
ri_client->send( EXCEPTIONS OTHERS = 1 ).
|
||||
IF sy-subrc <> 0.
|
||||
raise_comm_error_from_sy( ).
|
||||
ENDIF.
|
||||
|
||||
ri_client->receive( EXCEPTIONS OTHERS = 1 ).
|
||||
IF sy-subrc <> 0.
|
||||
raise_comm_error_from_sy( ).
|
||||
ENDIF.
|
||||
|
||||
" Check if authentication has succeeded
|
||||
ri_client->response->get_status(
|
||||
IMPORTING
|
||||
code = lv_http_code
|
||||
reason = lv_http_code_description ).
|
||||
IF lv_http_code <> 200.
|
||||
RAISE EXCEPTION TYPE zcx_abapgit_2fa_auth_failed
|
||||
EXPORTING
|
||||
mv_text = |Authentication failed: { lv_http_code_description }|.
|
||||
ENDIF.
|
||||
|
||||
" Cache the authenticated http session / client to avoid unnecessary additional authentication
|
||||
IF is_session_running( ) = abap_true.
|
||||
mi_authenticated_session = ri_client.
|
||||
ENDIF.
|
||||
ENDMETHOD.
|
||||
|
||||
|
||||
METHOD get_tobedel_tokens_from_resp.
|
||||
CONSTANTS: lc_search_regex TYPE string
|
||||
VALUE `\{"id": ?(\d+)[^\{]*"app":\{[^\{^\}]*\}[^\{]*"fingerprint": ?` &
|
||||
`"abapGit2FA"[^\{]*\}`.
|
||||
DATA: lv_response TYPE string,
|
||||
lo_regex TYPE REF TO cl_abap_regex,
|
||||
lo_matcher TYPE REF TO cl_abap_matcher.
|
||||
|
||||
lv_response = cl_abap_codepage=>convert_from( ii_response->get_data( ) ).
|
||||
|
||||
CREATE OBJECT lo_regex
|
||||
EXPORTING
|
||||
pattern = lc_search_regex.
|
||||
|
||||
lo_matcher = lo_regex->create_matcher( text = lv_response ).
|
||||
WHILE lo_matcher->find_next( ) = abap_true.
|
||||
APPEND lo_matcher->get_submatch( 1 ) TO rt_ids.
|
||||
ENDWHILE.
|
||||
ENDMETHOD.
|
||||
|
||||
|
||||
METHOD get_token_from_response.
|
||||
CONSTANTS: lc_search_regex TYPE string VALUE `.*"token":"([^"]*).*$`.
|
||||
DATA: lv_response TYPE string,
|
||||
lo_regex TYPE REF TO cl_abap_regex,
|
||||
lo_matcher TYPE REF TO cl_abap_matcher.
|
||||
|
||||
lv_response = cl_abap_codepage=>convert_from( ii_response->get_data( ) ).
|
||||
|
||||
CREATE OBJECT lo_regex
|
||||
EXPORTING
|
||||
pattern = lc_search_regex.
|
||||
|
||||
lo_matcher = lo_regex->create_matcher( text = lv_response ).
|
||||
IF lo_matcher->match( ) = abap_true.
|
||||
rv_token = lo_matcher->get_submatch( 1 ).
|
||||
ENDIF.
|
||||
ENDMETHOD.
|
||||
|
||||
|
||||
METHOD set_del_token_request.
|
||||
DATA: lv_url TYPE string.
|
||||
|
||||
lv_url = |{ gc_restendpoint_authorizations }/{ iv_token_id }|.
|
||||
|
||||
ii_request->set_header_field( name = if_http_header_fields_sap=>request_uri
|
||||
value = lv_url ).
|
||||
" Other methods than POST and GET do not have constants unfortunately
|
||||
" ii_request->set_method( if_http_request=>co_request_method_delete ).
|
||||
ii_request->set_method( 'DELETE' ).
|
||||
ENDMETHOD.
|
||||
|
||||
|
||||
METHOD set_list_token_request.
|
||||
ii_request->set_header_field( name = if_http_header_fields_sap=>request_uri
|
||||
value = gc_restendpoint_authorizations ).
|
||||
ii_request->set_method( if_http_request=>co_request_method_get ).
|
||||
ENDMETHOD.
|
||||
|
||||
|
||||
METHOD set_new_token_request.
|
||||
DATA: lv_json_string TYPE string.
|
||||
|
||||
lv_json_string = `{"scopes":["repo"],"note":"Generated by abapGit","fingerprint":"abapGit2FA"}`.
|
||||
|
||||
ii_request->set_data( cl_abap_codepage=>convert_to( lv_json_string ) ).
|
||||
ii_request->set_header_field( name = if_http_header_fields_sap=>request_uri
|
||||
value = gc_restendpoint_authorizations ).
|
||||
ii_request->set_method( if_http_request=>co_request_method_post ).
|
||||
ENDMETHOD.
|
||||
|
||||
|
||||
METHOD authenticate.
|
||||
DATA: li_http_client TYPE REF TO if_http_client,
|
||||
lv_http_code TYPE i,
|
||||
lv_http_code_description TYPE string.
|
||||
|
||||
" 1. Try to login to GitHub API
|
||||
li_http_client = get_authenticated_client( iv_username = iv_username
|
||||
iv_password = iv_password
|
||||
iv_2fa_token = iv_2fa_token ).
|
||||
|
||||
" 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_new_token_request( ii_request = li_http_client->request ).
|
||||
|
||||
li_http_client->send( EXCEPTIONS OTHERS = 1 ).
|
||||
IF sy-subrc <> 0.
|
||||
raise_comm_error_from_sy( ).
|
||||
ENDIF.
|
||||
|
||||
li_http_client->receive( EXCEPTIONS OTHERS = 1 ).
|
||||
IF sy-subrc <> 0.
|
||||
raise_comm_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 zcx_abapgit_2fa_gen_failed
|
||||
EXPORTING
|
||||
mv_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 zcx_abapgit_2fa_gen_failed
|
||||
EXPORTING
|
||||
mv_text = 'Token generation failed: parser error' ##NO_TEXT.
|
||||
ENDIF.
|
||||
|
||||
" GitHub might need some time until the new token is ready to use, give it a second
|
||||
CALL FUNCTION 'RZL_SLEEP'.
|
||||
ENDMETHOD.
|
||||
|
||||
|
||||
METHOD delete_access_tokens.
|
||||
DATA: li_http_client TYPE REF TO if_http_client,
|
||||
lv_http_code TYPE i,
|
||||
lv_http_code_description TYPE string,
|
||||
lt_tobedeleted_tokens TYPE stringtab.
|
||||
FIELD-SYMBOLS: <lv_id> TYPE string.
|
||||
|
||||
li_http_client = get_authenticated_client( iv_username = iv_username
|
||||
iv_password = iv_password
|
||||
iv_2fa_token = iv_2fa_token ).
|
||||
|
||||
set_list_token_request( li_http_client->request ).
|
||||
li_http_client->send( EXCEPTIONS OTHERS = 1 ).
|
||||
IF sy-subrc <> 0.
|
||||
raise_comm_error_from_sy( ).
|
||||
ENDIF.
|
||||
|
||||
li_http_client->receive( EXCEPTIONS OTHERS = 1 ).
|
||||
IF sy-subrc <> 0.
|
||||
raise_comm_error_from_sy( ).
|
||||
ENDIF.
|
||||
|
||||
li_http_client->response->get_status(
|
||||
IMPORTING
|
||||
code = lv_http_code
|
||||
reason = lv_http_code_description ).
|
||||
IF lv_http_code <> 200.
|
||||
RAISE EXCEPTION TYPE zcx_abapgit_2fa_del_failed
|
||||
EXPORTING
|
||||
mv_text = |Could not fetch current 2FA authorizations: | &&
|
||||
|{ lv_http_code } { lv_http_code_description }|.
|
||||
ENDIF.
|
||||
|
||||
lt_tobedeleted_tokens = get_tobedel_tokens_from_resp( li_http_client->response ).
|
||||
LOOP AT lt_tobedeleted_tokens ASSIGNING <lv_id> WHERE table_line IS NOT INITIAL.
|
||||
set_del_token_request( ii_request = li_http_client->request
|
||||
iv_token_id = <lv_id> ).
|
||||
li_http_client->send( EXCEPTIONS OTHERS = 1 ).
|
||||
IF sy-subrc <> 0.
|
||||
raise_comm_error_from_sy( ).
|
||||
ENDIF.
|
||||
|
||||
li_http_client->receive( EXCEPTIONS OTHERS = 1 ).
|
||||
IF sy-subrc <> 0.
|
||||
raise_comm_error_from_sy( ).
|
||||
ENDIF.
|
||||
|
||||
li_http_client->response->get_status(
|
||||
IMPORTING
|
||||
code = lv_http_code
|
||||
reason = lv_http_code_description ).
|
||||
IF lv_http_code <> 204.
|
||||
RAISE EXCEPTION TYPE zcx_abapgit_2fa_del_failed
|
||||
EXPORTING
|
||||
mv_text = |Could not delete token '{ <lv_id> }': | &&
|
||||
|{ lv_http_code } { lv_http_code_description }|.
|
||||
ENDIF.
|
||||
ENDLOOP.
|
||||
ENDMETHOD.
|
||||
|
||||
|
||||
METHOD end.
|
||||
super->end( ).
|
||||
FREE mi_authenticated_session.
|
||||
ENDMETHOD.
|
||||
|
||||
|
||||
METHOD get_service_id_from_url.
|
||||
rv_id = 'github'.
|
||||
ENDMETHOD.
|
||||
|
||||
|
||||
METHOD is_2fa_required.
|
||||
|
||||
DATA: li_client TYPE REF TO if_http_client,
|
||||
lo_proxy TYPE REF TO zcl_abapgit_proxy_config.
|
||||
|
||||
|
||||
CREATE OBJECT lo_proxy.
|
||||
|
||||
cl_http_client=>create_by_url(
|
||||
EXPORTING
|
||||
url = gc_github_api_url
|
||||
ssl_id = 'ANONYM'
|
||||
proxy_host = lo_proxy->get_proxy_url( )
|
||||
proxy_service = lo_proxy->get_proxy_port( )
|
||||
IMPORTING
|
||||
client = li_client
|
||||
EXCEPTIONS
|
||||
argument_not_found = 1
|
||||
plugin_not_active = 2
|
||||
internal_error = 3
|
||||
OTHERS = 4 ).
|
||||
IF sy-subrc <> 0.
|
||||
raise_comm_error_from_sy( ).
|
||||
ENDIF.
|
||||
|
||||
li_client->propertytype_logon_popup = if_http_client=>co_disabled.
|
||||
|
||||
" The request needs to use something other than GET and it needs to be send to an endpoint
|
||||
" to trigger a SMS.
|
||||
li_client->request->set_header_field( name = if_http_header_fields_sap=>request_uri
|
||||
value = gc_restendpoint_authorizations ).
|
||||
li_client->request->set_method( if_http_request=>co_request_method_post ).
|
||||
|
||||
" Try to authenticate, if 2FA is required there will be a specific response header
|
||||
li_client->authenticate( username = iv_username password = iv_password ).
|
||||
|
||||
li_client->send( EXCEPTIONS OTHERS = 1 ).
|
||||
IF sy-subrc <> 0.
|
||||
raise_comm_error_from_sy( ).
|
||||
ENDIF.
|
||||
|
||||
li_client->receive( EXCEPTIONS OTHERS = 1 ).
|
||||
IF sy-subrc <> 0.
|
||||
raise_comm_error_from_sy( ).
|
||||
ENDIF.
|
||||
|
||||
" The response will either be UNAUTHORIZED or MALFORMED which is both fine.
|
||||
|
||||
IF li_client->response->get_header_field( gc_otp_header_name ) CP 'required*'.
|
||||
rv_required = abap_true.
|
||||
ENDIF.
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
19
src/http/zcl_abapgit_2fa_github_auth.clas.xml
Normal file
19
src/http/zcl_abapgit_2fa_github_auth.clas.xml
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<abapGit version="v1.0.0" serializer="LCL_OBJECT_CLAS" serializer_version="v1.0.0">
|
||||
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
|
||||
<asx:values>
|
||||
<VSEOCLASS>
|
||||
<CLSNAME>ZCL_ABAPGIT_2FA_GITHUB_AUTH</CLSNAME>
|
||||
<VERSION>1</VERSION>
|
||||
<LANGU>E</LANGU>
|
||||
<DESCRIPT>2FA for github</DESCRIPT>
|
||||
<EXPOSURE>2</EXPOSURE>
|
||||
<STATE>1</STATE>
|
||||
<CLSFINAL>X</CLSFINAL>
|
||||
<CLSCCINCL>X</CLSCCINCL>
|
||||
<FIXPT>X</FIXPT>
|
||||
<UNICODE>X</UNICODE>
|
||||
</VSEOCLASS>
|
||||
</asx:values>
|
||||
</asx:abap>
|
||||
</abapGit>
|
53
src/http/zcl_abapgit_proxy_auth.clas.abap
Normal file
53
src/http/zcl_abapgit_proxy_auth.clas.abap
Normal file
|
@ -0,0 +1,53 @@
|
|||
CLASS zcl_abapgit_proxy_auth DEFINITION
|
||||
PUBLIC
|
||||
FINAL
|
||||
CREATE PUBLIC .
|
||||
|
||||
PUBLIC SECTION.
|
||||
CLASS-METHODS:
|
||||
run
|
||||
IMPORTING ii_client TYPE REF TO if_http_client
|
||||
RAISING zcx_abapgit_exception.
|
||||
|
||||
PRIVATE SECTION.
|
||||
CLASS-DATA: gv_username TYPE string,
|
||||
gv_password TYPE string.
|
||||
|
||||
CLASS-METHODS: enter RAISING zcx_abapgit_exception.
|
||||
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
|
||||
CLASS ZCL_ABAPGIT_PROXY_AUTH IMPLEMENTATION.
|
||||
|
||||
|
||||
METHOD enter.
|
||||
|
||||
zcl_abapgit_password_dialog=>popup(
|
||||
EXPORTING
|
||||
iv_repo_url = 'Proxy Authentication'
|
||||
CHANGING
|
||||
cv_user = gv_username
|
||||
cv_pass = gv_password ).
|
||||
|
||||
IF gv_username IS INITIAL OR gv_password IS INITIAL.
|
||||
zcx_abapgit_exception=>raise( 'Proxy auth failed' ).
|
||||
ENDIF.
|
||||
|
||||
ENDMETHOD.
|
||||
|
||||
|
||||
METHOD run.
|
||||
|
||||
IF gv_username IS INITIAL OR gv_password IS INITIAL.
|
||||
enter( ).
|
||||
ENDIF.
|
||||
|
||||
ii_client->authenticate(
|
||||
proxy_authentication = abap_true
|
||||
username = gv_username
|
||||
password = gv_password ).
|
||||
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
19
src/http/zcl_abapgit_proxy_auth.clas.xml
Normal file
19
src/http/zcl_abapgit_proxy_auth.clas.xml
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<abapGit version="v1.0.0" serializer="LCL_OBJECT_CLAS" serializer_version="v1.0.0">
|
||||
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
|
||||
<asx:values>
|
||||
<VSEOCLASS>
|
||||
<CLSNAME>ZCL_ABAPGIT_PROXY_AUTH</CLSNAME>
|
||||
<VERSION>1</VERSION>
|
||||
<LANGU>E</LANGU>
|
||||
<DESCRIPT>Proxy auth</DESCRIPT>
|
||||
<EXPOSURE>2</EXPOSURE>
|
||||
<STATE>1</STATE>
|
||||
<CLSFINAL>X</CLSFINAL>
|
||||
<CLSCCINCL>X</CLSCCINCL>
|
||||
<FIXPT>X</FIXPT>
|
||||
<UNICODE>X</UNICODE>
|
||||
</VSEOCLASS>
|
||||
</asx:values>
|
||||
</asx:abap>
|
||||
</abapGit>
|
39
src/http/zcx_abapgit_2fa_auth_failed.clas.abap
Normal file
39
src/http/zcx_abapgit_2fa_auth_failed.clas.abap
Normal file
|
@ -0,0 +1,39 @@
|
|||
class ZCX_ABAPGIT_2FA_AUTH_FAILED definition
|
||||
public
|
||||
inheriting from ZCX_ABAPGIT_2FA_ERROR
|
||||
final
|
||||
create public .
|
||||
|
||||
public section.
|
||||
|
||||
methods CONSTRUCTOR
|
||||
importing
|
||||
!TEXTID like TEXTID optional
|
||||
!PREVIOUS like PREVIOUS optional
|
||||
!MV_TEXT type STRING optional .
|
||||
protected section.
|
||||
|
||||
methods GET_DEFAULT_TEXT
|
||||
redefinition .
|
||||
private section.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
|
||||
CLASS ZCX_ABAPGIT_2FA_AUTH_FAILED IMPLEMENTATION.
|
||||
|
||||
|
||||
method CONSTRUCTOR.
|
||||
CALL METHOD SUPER->CONSTRUCTOR
|
||||
EXPORTING
|
||||
TEXTID = TEXTID
|
||||
PREVIOUS = PREVIOUS
|
||||
MV_TEXT = MV_TEXT
|
||||
.
|
||||
endmethod.
|
||||
|
||||
|
||||
METHOD get_default_text.
|
||||
rv_text = 'Authentication failed using 2FA.' ##NO_TEXT.
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
28
src/http/zcx_abapgit_2fa_auth_failed.clas.xml
Normal file
28
src/http/zcx_abapgit_2fa_auth_failed.clas.xml
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<abapGit version="v1.0.0" serializer="LCL_OBJECT_CLAS" serializer_version="v1.0.0">
|
||||
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
|
||||
<asx:values>
|
||||
<VSEOCLASS>
|
||||
<CLSNAME>ZCX_ABAPGIT_2FA_AUTH_FAILED</CLSNAME>
|
||||
<VERSION>1</VERSION>
|
||||
<LANGU>E</LANGU>
|
||||
<DESCRIPT>Auth failed</DESCRIPT>
|
||||
<CATEGORY>40</CATEGORY>
|
||||
<EXPOSURE>2</EXPOSURE>
|
||||
<STATE>1</STATE>
|
||||
<CLSFINAL>X</CLSFINAL>
|
||||
<CLSCCINCL>X</CLSCCINCL>
|
||||
<FIXPT>X</FIXPT>
|
||||
<UNICODE>X</UNICODE>
|
||||
</VSEOCLASS>
|
||||
<DESCRIPTIONS>
|
||||
<SEOCOMPOTX>
|
||||
<CLSNAME>ZCX_ABAPGIT_2FA_AUTH_FAILED</CLSNAME>
|
||||
<CMPNAME>CONSTRUCTOR</CMPNAME>
|
||||
<LANGU>E</LANGU>
|
||||
<DESCRIPT>CONSTRUCTOR</DESCRIPT>
|
||||
</SEOCOMPOTX>
|
||||
</DESCRIPTIONS>
|
||||
</asx:values>
|
||||
</asx:abap>
|
||||
</abapGit>
|
39
src/http/zcx_abapgit_2fa_comm_error.clas.abap
Normal file
39
src/http/zcx_abapgit_2fa_comm_error.clas.abap
Normal file
|
@ -0,0 +1,39 @@
|
|||
class ZCX_ABAPGIT_2FA_COMM_ERROR definition
|
||||
public
|
||||
inheriting from ZCX_ABAPGIT_2FA_ERROR
|
||||
final
|
||||
create public .
|
||||
|
||||
public section.
|
||||
|
||||
methods CONSTRUCTOR
|
||||
importing
|
||||
!TEXTID like TEXTID optional
|
||||
!PREVIOUS like PREVIOUS optional
|
||||
!MV_TEXT type STRING optional .
|
||||
protected section.
|
||||
|
||||
methods GET_DEFAULT_TEXT
|
||||
redefinition .
|
||||
private section.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
|
||||
CLASS ZCX_ABAPGIT_2FA_COMM_ERROR IMPLEMENTATION.
|
||||
|
||||
|
||||
method CONSTRUCTOR.
|
||||
CALL METHOD SUPER->CONSTRUCTOR
|
||||
EXPORTING
|
||||
TEXTID = TEXTID
|
||||
PREVIOUS = PREVIOUS
|
||||
MV_TEXT = MV_TEXT
|
||||
.
|
||||
endmethod.
|
||||
|
||||
|
||||
METHOD get_default_text.
|
||||
rv_text = 'Communication error.' ##NO_TEXT.
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
28
src/http/zcx_abapgit_2fa_comm_error.clas.xml
Normal file
28
src/http/zcx_abapgit_2fa_comm_error.clas.xml
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<abapGit version="v1.0.0" serializer="LCL_OBJECT_CLAS" serializer_version="v1.0.0">
|
||||
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
|
||||
<asx:values>
|
||||
<VSEOCLASS>
|
||||
<CLSNAME>ZCX_ABAPGIT_2FA_COMM_ERROR</CLSNAME>
|
||||
<VERSION>1</VERSION>
|
||||
<LANGU>E</LANGU>
|
||||
<DESCRIPT>comm error</DESCRIPT>
|
||||
<CATEGORY>40</CATEGORY>
|
||||
<EXPOSURE>2</EXPOSURE>
|
||||
<STATE>1</STATE>
|
||||
<CLSFINAL>X</CLSFINAL>
|
||||
<CLSCCINCL>X</CLSCCINCL>
|
||||
<FIXPT>X</FIXPT>
|
||||
<UNICODE>X</UNICODE>
|
||||
</VSEOCLASS>
|
||||
<DESCRIPTIONS>
|
||||
<SEOCOMPOTX>
|
||||
<CLSNAME>ZCX_ABAPGIT_2FA_COMM_ERROR</CLSNAME>
|
||||
<CMPNAME>CONSTRUCTOR</CMPNAME>
|
||||
<LANGU>E</LANGU>
|
||||
<DESCRIPT>CONSTRUCTOR</DESCRIPT>
|
||||
</SEOCOMPOTX>
|
||||
</DESCRIPTIONS>
|
||||
</asx:values>
|
||||
</asx:abap>
|
||||
</abapGit>
|
39
src/http/zcx_abapgit_2fa_del_failed.clas.abap
Normal file
39
src/http/zcx_abapgit_2fa_del_failed.clas.abap
Normal file
|
@ -0,0 +1,39 @@
|
|||
class ZCX_ABAPGIT_2FA_DEL_FAILED definition
|
||||
public
|
||||
inheriting from ZCX_ABAPGIT_2FA_ERROR
|
||||
final
|
||||
create public .
|
||||
|
||||
public section.
|
||||
|
||||
methods CONSTRUCTOR
|
||||
importing
|
||||
!TEXTID like TEXTID optional
|
||||
!PREVIOUS like PREVIOUS optional
|
||||
!MV_TEXT type STRING optional .
|
||||
protected section.
|
||||
|
||||
methods GET_DEFAULT_TEXT
|
||||
redefinition .
|
||||
private section.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
|
||||
CLASS ZCX_ABAPGIT_2FA_DEL_FAILED IMPLEMENTATION.
|
||||
|
||||
|
||||
method CONSTRUCTOR.
|
||||
CALL METHOD SUPER->CONSTRUCTOR
|
||||
EXPORTING
|
||||
TEXTID = TEXTID
|
||||
PREVIOUS = PREVIOUS
|
||||
MV_TEXT = MV_TEXT
|
||||
.
|
||||
endmethod.
|
||||
|
||||
|
||||
METHOD get_default_text.
|
||||
rv_text = 'Deleting previous access tokens failed.' ##NO_TEXT.
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
28
src/http/zcx_abapgit_2fa_del_failed.clas.xml
Normal file
28
src/http/zcx_abapgit_2fa_del_failed.clas.xml
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<abapGit version="v1.0.0" serializer="LCL_OBJECT_CLAS" serializer_version="v1.0.0">
|
||||
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
|
||||
<asx:values>
|
||||
<VSEOCLASS>
|
||||
<CLSNAME>ZCX_ABAPGIT_2FA_DEL_FAILED</CLSNAME>
|
||||
<VERSION>1</VERSION>
|
||||
<LANGU>E</LANGU>
|
||||
<DESCRIPT>del failed</DESCRIPT>
|
||||
<CATEGORY>40</CATEGORY>
|
||||
<EXPOSURE>2</EXPOSURE>
|
||||
<STATE>1</STATE>
|
||||
<CLSFINAL>X</CLSFINAL>
|
||||
<CLSCCINCL>X</CLSCCINCL>
|
||||
<FIXPT>X</FIXPT>
|
||||
<UNICODE>X</UNICODE>
|
||||
</VSEOCLASS>
|
||||
<DESCRIPTIONS>
|
||||
<SEOCOMPOTX>
|
||||
<CLSNAME>ZCX_ABAPGIT_2FA_DEL_FAILED</CLSNAME>
|
||||
<CMPNAME>CONSTRUCTOR</CMPNAME>
|
||||
<LANGU>E</LANGU>
|
||||
<DESCRIPT>CONSTRUCTOR</DESCRIPT>
|
||||
</SEOCOMPOTX>
|
||||
</DESCRIPTIONS>
|
||||
</asx:values>
|
||||
</asx:abap>
|
||||
</abapGit>
|
59
src/http/zcx_abapgit_2fa_error.clas.abap
Normal file
59
src/http/zcx_abapgit_2fa_error.clas.abap
Normal file
|
@ -0,0 +1,59 @@
|
|||
class ZCX_ABAPGIT_2FA_ERROR definition
|
||||
public
|
||||
inheriting from CX_STATIC_CHECK
|
||||
create public .
|
||||
|
||||
public section.
|
||||
|
||||
data MV_TEXT type STRING read-only .
|
||||
|
||||
methods CONSTRUCTOR
|
||||
importing
|
||||
!TEXTID like TEXTID optional
|
||||
!PREVIOUS like PREVIOUS optional
|
||||
!MV_TEXT type STRING optional .
|
||||
|
||||
methods IF_MESSAGE~GET_TEXT
|
||||
redefinition .
|
||||
protected section.
|
||||
|
||||
methods GET_DEFAULT_TEXT
|
||||
returning
|
||||
value(RV_TEXT) type STRING .
|
||||
private section.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
|
||||
CLASS ZCX_ABAPGIT_2FA_ERROR IMPLEMENTATION.
|
||||
|
||||
|
||||
method CONSTRUCTOR.
|
||||
CALL METHOD SUPER->CONSTRUCTOR
|
||||
EXPORTING
|
||||
TEXTID = TEXTID
|
||||
PREVIOUS = PREVIOUS
|
||||
.
|
||||
me->MV_TEXT = MV_TEXT .
|
||||
endmethod.
|
||||
|
||||
|
||||
METHOD get_default_text.
|
||||
|
||||
rv_text = 'Error in two factor authentication.' ##NO_TEXT.
|
||||
|
||||
ENDMETHOD.
|
||||
|
||||
|
||||
METHOD if_message~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.
|
||||
ENDCLASS.
|
27
src/http/zcx_abapgit_2fa_error.clas.xml
Normal file
27
src/http/zcx_abapgit_2fa_error.clas.xml
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<abapGit version="v1.0.0" serializer="LCL_OBJECT_CLAS" serializer_version="v1.0.0">
|
||||
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
|
||||
<asx:values>
|
||||
<VSEOCLASS>
|
||||
<CLSNAME>ZCX_ABAPGIT_2FA_ERROR</CLSNAME>
|
||||
<VERSION>1</VERSION>
|
||||
<LANGU>E</LANGU>
|
||||
<DESCRIPT>Error</DESCRIPT>
|
||||
<CATEGORY>40</CATEGORY>
|
||||
<EXPOSURE>2</EXPOSURE>
|
||||
<STATE>1</STATE>
|
||||
<CLSCCINCL>X</CLSCCINCL>
|
||||
<FIXPT>X</FIXPT>
|
||||
<UNICODE>X</UNICODE>
|
||||
</VSEOCLASS>
|
||||
<DESCRIPTIONS>
|
||||
<SEOCOMPOTX>
|
||||
<CLSNAME>ZCX_ABAPGIT_2FA_ERROR</CLSNAME>
|
||||
<CMPNAME>CONSTRUCTOR</CMPNAME>
|
||||
<LANGU>E</LANGU>
|
||||
<DESCRIPT>CONSTRUCTOR</DESCRIPT>
|
||||
</SEOCOMPOTX>
|
||||
</DESCRIPTIONS>
|
||||
</asx:values>
|
||||
</asx:abap>
|
||||
</abapGit>
|
39
src/http/zcx_abapgit_2fa_gen_failed.clas.abap
Normal file
39
src/http/zcx_abapgit_2fa_gen_failed.clas.abap
Normal file
|
@ -0,0 +1,39 @@
|
|||
class ZCX_ABAPGIT_2FA_GEN_FAILED definition
|
||||
public
|
||||
inheriting from ZCX_ABAPGIT_2FA_ERROR
|
||||
final
|
||||
create public .
|
||||
|
||||
public section.
|
||||
|
||||
methods CONSTRUCTOR
|
||||
importing
|
||||
!TEXTID like TEXTID optional
|
||||
!PREVIOUS like PREVIOUS optional
|
||||
!MV_TEXT type STRING optional .
|
||||
protected section.
|
||||
|
||||
methods GET_DEFAULT_TEXT
|
||||
redefinition .
|
||||
private section.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
|
||||
CLASS ZCX_ABAPGIT_2FA_GEN_FAILED IMPLEMENTATION.
|
||||
|
||||
|
||||
method CONSTRUCTOR.
|
||||
CALL METHOD SUPER->CONSTRUCTOR
|
||||
EXPORTING
|
||||
TEXTID = TEXTID
|
||||
PREVIOUS = PREVIOUS
|
||||
MV_TEXT = MV_TEXT
|
||||
.
|
||||
endmethod.
|
||||
|
||||
|
||||
METHOD get_default_text.
|
||||
rv_text = 'Two factor access token generation failed.' ##NO_TEXT.
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
28
src/http/zcx_abapgit_2fa_gen_failed.clas.xml
Normal file
28
src/http/zcx_abapgit_2fa_gen_failed.clas.xml
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<abapGit version="v1.0.0" serializer="LCL_OBJECT_CLAS" serializer_version="v1.0.0">
|
||||
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
|
||||
<asx:values>
|
||||
<VSEOCLASS>
|
||||
<CLSNAME>ZCX_ABAPGIT_2FA_GEN_FAILED</CLSNAME>
|
||||
<VERSION>1</VERSION>
|
||||
<LANGU>E</LANGU>
|
||||
<DESCRIPT>gen failed</DESCRIPT>
|
||||
<CATEGORY>40</CATEGORY>
|
||||
<EXPOSURE>2</EXPOSURE>
|
||||
<STATE>1</STATE>
|
||||
<CLSFINAL>X</CLSFINAL>
|
||||
<CLSCCINCL>X</CLSCCINCL>
|
||||
<FIXPT>X</FIXPT>
|
||||
<UNICODE>X</UNICODE>
|
||||
</VSEOCLASS>
|
||||
<DESCRIPTIONS>
|
||||
<SEOCOMPOTX>
|
||||
<CLSNAME>ZCX_ABAPGIT_2FA_GEN_FAILED</CLSNAME>
|
||||
<CMPNAME>CONSTRUCTOR</CMPNAME>
|
||||
<LANGU>E</LANGU>
|
||||
<DESCRIPT>CONSTRUCTOR</DESCRIPT>
|
||||
</SEOCOMPOTX>
|
||||
</DESCRIPTIONS>
|
||||
</asx:values>
|
||||
</asx:abap>
|
||||
</abapGit>
|
39
src/http/zcx_abapgit_2fa_illegal_state.clas.abap
Normal file
39
src/http/zcx_abapgit_2fa_illegal_state.clas.abap
Normal file
|
@ -0,0 +1,39 @@
|
|||
class ZCX_ABAPGIT_2FA_ILLEGAL_STATE definition
|
||||
public
|
||||
inheriting from ZCX_ABAPGIT_2FA_ERROR
|
||||
final
|
||||
create public .
|
||||
|
||||
public section.
|
||||
|
||||
methods CONSTRUCTOR
|
||||
importing
|
||||
!TEXTID like TEXTID optional
|
||||
!PREVIOUS like PREVIOUS optional
|
||||
!MV_TEXT type STRING optional .
|
||||
protected section.
|
||||
|
||||
methods GET_DEFAULT_TEXT
|
||||
redefinition .
|
||||
private section.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
|
||||
CLASS ZCX_ABAPGIT_2FA_ILLEGAL_STATE IMPLEMENTATION.
|
||||
|
||||
|
||||
method CONSTRUCTOR.
|
||||
CALL METHOD SUPER->CONSTRUCTOR
|
||||
EXPORTING
|
||||
TEXTID = TEXTID
|
||||
PREVIOUS = PREVIOUS
|
||||
MV_TEXT = MV_TEXT
|
||||
.
|
||||
endmethod.
|
||||
|
||||
|
||||
METHOD get_default_text.
|
||||
rv_text = 'Illegal state.' ##NO_TEXT.
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
28
src/http/zcx_abapgit_2fa_illegal_state.clas.xml
Normal file
28
src/http/zcx_abapgit_2fa_illegal_state.clas.xml
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<abapGit version="v1.0.0" serializer="LCL_OBJECT_CLAS" serializer_version="v1.0.0">
|
||||
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
|
||||
<asx:values>
|
||||
<VSEOCLASS>
|
||||
<CLSNAME>ZCX_ABAPGIT_2FA_ILLEGAL_STATE</CLSNAME>
|
||||
<VERSION>1</VERSION>
|
||||
<LANGU>E</LANGU>
|
||||
<DESCRIPT>illegal state</DESCRIPT>
|
||||
<CATEGORY>40</CATEGORY>
|
||||
<EXPOSURE>2</EXPOSURE>
|
||||
<STATE>1</STATE>
|
||||
<CLSFINAL>X</CLSFINAL>
|
||||
<CLSCCINCL>X</CLSCCINCL>
|
||||
<FIXPT>X</FIXPT>
|
||||
<UNICODE>X</UNICODE>
|
||||
</VSEOCLASS>
|
||||
<DESCRIPTIONS>
|
||||
<SEOCOMPOTX>
|
||||
<CLSNAME>ZCX_ABAPGIT_2FA_ILLEGAL_STATE</CLSNAME>
|
||||
<CMPNAME>CONSTRUCTOR</CMPNAME>
|
||||
<LANGU>E</LANGU>
|
||||
<DESCRIPT>CONSTRUCTOR</DESCRIPT>
|
||||
</SEOCOMPOTX>
|
||||
</DESCRIPTIONS>
|
||||
</asx:values>
|
||||
</asx:abap>
|
||||
</abapGit>
|
39
src/http/zcx_abapgit_2fa_unsupported.clas.abap
Normal file
39
src/http/zcx_abapgit_2fa_unsupported.clas.abap
Normal file
|
@ -0,0 +1,39 @@
|
|||
class ZCX_ABAPGIT_2FA_UNSUPPORTED definition
|
||||
public
|
||||
inheriting from ZCX_ABAPGIT_2FA_ERROR
|
||||
final
|
||||
create public .
|
||||
|
||||
public section.
|
||||
|
||||
methods CONSTRUCTOR
|
||||
importing
|
||||
!TEXTID like TEXTID optional
|
||||
!PREVIOUS like PREVIOUS optional
|
||||
!MV_TEXT type STRING optional .
|
||||
protected section.
|
||||
|
||||
methods GET_DEFAULT_TEXT
|
||||
redefinition .
|
||||
private section.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
|
||||
CLASS ZCX_ABAPGIT_2FA_UNSUPPORTED IMPLEMENTATION.
|
||||
|
||||
|
||||
method CONSTRUCTOR.
|
||||
CALL METHOD SUPER->CONSTRUCTOR
|
||||
EXPORTING
|
||||
TEXTID = TEXTID
|
||||
PREVIOUS = PREVIOUS
|
||||
MV_TEXT = MV_TEXT
|
||||
.
|
||||
endmethod.
|
||||
|
||||
|
||||
METHOD get_default_text.
|
||||
rv_text = 'The service is not supported for two factor authentication.' ##NO_TEXT.
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
28
src/http/zcx_abapgit_2fa_unsupported.clas.xml
Normal file
28
src/http/zcx_abapgit_2fa_unsupported.clas.xml
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<abapGit version="v1.0.0" serializer="LCL_OBJECT_CLAS" serializer_version="v1.0.0">
|
||||
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
|
||||
<asx:values>
|
||||
<VSEOCLASS>
|
||||
<CLSNAME>ZCX_ABAPGIT_2FA_UNSUPPORTED</CLSNAME>
|
||||
<VERSION>1</VERSION>
|
||||
<LANGU>E</LANGU>
|
||||
<DESCRIPT>Unsupported</DESCRIPT>
|
||||
<CATEGORY>40</CATEGORY>
|
||||
<EXPOSURE>2</EXPOSURE>
|
||||
<STATE>1</STATE>
|
||||
<CLSFINAL>X</CLSFINAL>
|
||||
<CLSCCINCL>X</CLSCCINCL>
|
||||
<FIXPT>X</FIXPT>
|
||||
<UNICODE>X</UNICODE>
|
||||
</VSEOCLASS>
|
||||
<DESCRIPTIONS>
|
||||
<SEOCOMPOTX>
|
||||
<CLSNAME>ZCX_ABAPGIT_2FA_UNSUPPORTED</CLSNAME>
|
||||
<CMPNAME>CONSTRUCTOR</CMPNAME>
|
||||
<LANGU>E</LANGU>
|
||||
<DESCRIPT>CONSTRUCTOR</DESCRIPT>
|
||||
</SEOCOMPOTX>
|
||||
</DESCRIPTIONS>
|
||||
</asx:values>
|
||||
</asx:abap>
|
||||
</abapGit>
|
79
src/http/zif_abapgit_2fa_authenticator.intf.abap
Normal file
79
src/http/zif_abapgit_2fa_authenticator.intf.abap
Normal file
|
@ -0,0 +1,79 @@
|
|||
"! 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.
|
||||
"! </p>
|
||||
"! <p>
|
||||
"! <em>LCL_2FA_AUTHENTICATOR_REGISTRY</em> can be used to find a suitable implementation for a given
|
||||
"! repository.
|
||||
"! </p>
|
||||
"! <p>
|
||||
"! Using the <em>begin</em> and <em>end</em> methods an internal session can be started and
|
||||
"! completed in which internal state necessary for multiple methods will be cached. This can be
|
||||
"! used to avoid having multiple http sessions between <em>authenticate</em> and
|
||||
"! <em>delete_access_tokens</em>.
|
||||
"! </p>
|
||||
INTERFACE zif_abapgit_2fa_authenticator PUBLIC.
|
||||
|
||||
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 zcx_abapgit_2fa_auth_failed
|
||||
zcx_abapgit_2fa_gen_failed
|
||||
zcx_abapgit_2fa_comm_error,
|
||||
"! 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
|
||||
"! @raising lcx_2fa_unsupported | Url is not supported
|
||||
get_service_id_from_url IMPORTING iv_url TYPE string
|
||||
RETURNING VALUE(rv_id) TYPE string
|
||||
RAISING zcx_abapgit_2fa_unsupported,
|
||||
"! Check if two factor authentication is required
|
||||
"! @parameter iv_url | Repository url
|
||||
"! @parameter iv_username | Username
|
||||
"! @parameter iv_password | Password
|
||||
"! @parameter rv_required | 2FA is required
|
||||
is_2fa_required IMPORTING iv_url TYPE string
|
||||
iv_username TYPE string
|
||||
iv_password TYPE string
|
||||
RETURNING VALUE(rv_required) TYPE abap_bool
|
||||
RAISING zcx_abapgit_2fa_comm_error,
|
||||
"! Delete all previously created access tokens for abapGit
|
||||
"! @parameter iv_url | Repository url
|
||||
"! @parameter iv_username | Username
|
||||
"! @parameter iv_password | Password
|
||||
"! @parameter iv_2fa_token | Two factor token
|
||||
"! @raising lcx_2fa_token_del_failed | Token deletion failed
|
||||
"! @raising lcx_2fa_auth_failed | Authentication failed
|
||||
delete_access_tokens IMPORTING iv_url TYPE string
|
||||
iv_username TYPE string
|
||||
iv_password TYPE string
|
||||
iv_2fa_token TYPE string
|
||||
RAISING zcx_abapgit_2fa_del_failed
|
||||
zcx_abapgit_2fa_comm_error
|
||||
zcx_abapgit_2fa_auth_failed,
|
||||
"! Begin an authenticator session that uses internal caching for authorizations
|
||||
"! @raising lcx_2fa_illegal_state | Session already started
|
||||
begin RAISING zcx_abapgit_2fa_illegal_state,
|
||||
"! End an authenticator session and clear internal caches
|
||||
"! @raising lcx_2fa_illegal_state | Session not running
|
||||
end RAISING zcx_abapgit_2fa_illegal_state.
|
||||
ENDINTERFACE.
|
16
src/http/zif_abapgit_2fa_authenticator.intf.xml
Normal file
16
src/http/zif_abapgit_2fa_authenticator.intf.xml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<abapGit version="v1.0.0" serializer="LCL_OBJECT_INTF" serializer_version="v1.0.0">
|
||||
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
|
||||
<asx:values>
|
||||
<VSEOINTERF>
|
||||
<CLSNAME>ZIF_ABAPGIT_2FA_AUTHENTICATOR</CLSNAME>
|
||||
<VERSION>1</VERSION>
|
||||
<LANGU>E</LANGU>
|
||||
<DESCRIPT>2FA authenticator</DESCRIPT>
|
||||
<EXPOSURE>2</EXPOSURE>
|
||||
<STATE>1</STATE>
|
||||
<UNICODE>X</UNICODE>
|
||||
</VSEOINTERF>
|
||||
</asx:values>
|
||||
</asx:abap>
|
||||
</abapGit>
|
|
@ -2,800 +2,4 @@
|
|||
*& 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_gen_failed DEFINITION INHERITING FROM lcx_2fa_error FINAL.
|
||||
PROTECTED SECTION.
|
||||
METHODS:
|
||||
get_default_text REDEFINITION.
|
||||
ENDCLASS.
|
||||
|
||||
CLASS lcx_2fa_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_del_failed DEFINITION INHERITING FROM lcx_2fa_error FINAL.
|
||||
PROTECTED SECTION.
|
||||
METHODS:
|
||||
get_default_text REDEFINITION.
|
||||
ENDCLASS.
|
||||
|
||||
CLASS lcx_2fa_del_failed IMPLEMENTATION.
|
||||
METHOD get_default_text.
|
||||
rv_text = 'Deleting previous access tokens failed.' ##NO_TEXT.
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
|
||||
CLASS lcx_2fa_comm_error DEFINITION INHERITING FROM lcx_2fa_error FINAL.
|
||||
PROTECTED SECTION.
|
||||
METHODS:
|
||||
get_default_text REDEFINITION.
|
||||
ENDCLASS.
|
||||
|
||||
CLASS lcx_2fa_comm_error IMPLEMENTATION.
|
||||
METHOD get_default_text.
|
||||
rv_text = 'Communication error.' ##NO_TEXT.
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
|
||||
CLASS lcx_2fa_illegal_state DEFINITION INHERITING FROM lcx_2fa_error FINAL.
|
||||
PROTECTED SECTION.
|
||||
METHODS:
|
||||
get_default_text REDEFINITION.
|
||||
ENDCLASS.
|
||||
|
||||
CLASS lcx_2fa_illegal_state IMPLEMENTATION.
|
||||
METHOD get_default_text.
|
||||
rv_text = 'Illegal state.' ##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.
|
||||
"! </p>
|
||||
"! <p>
|
||||
"! <em>LCL_2FA_AUTHENTICATOR_REGISTRY</em> can be used to find a suitable implementation for a given
|
||||
"! repository.
|
||||
"! </p>
|
||||
"! <p>
|
||||
"! Using the <em>begin</em> and <em>end</em> methods an internal session can be started and
|
||||
"! completed in which internal state necessary for multiple methods will be cached. This can be
|
||||
"! used to avoid having multiple http sessions between <em>authenticate</em> and
|
||||
"! <em>delete_access_tokens</em>.
|
||||
"! </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_gen_failed
|
||||
lcx_2fa_comm_error,
|
||||
"! 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
|
||||
"! @raising lcx_2fa_unsupported | Url is not supported
|
||||
get_service_id_from_url IMPORTING iv_url TYPE string
|
||||
RETURNING VALUE(rv_id) TYPE string
|
||||
RAISING lcx_2fa_unsupported,
|
||||
"! Check if two factor authentication is required
|
||||
"! @parameter iv_url | Repository url
|
||||
"! @parameter iv_username | Username
|
||||
"! @parameter iv_password | Password
|
||||
"! @parameter rv_required | 2FA is required
|
||||
is_2fa_required IMPORTING iv_url TYPE string
|
||||
iv_username TYPE string
|
||||
iv_password TYPE string
|
||||
RETURNING VALUE(rv_required) TYPE abap_bool
|
||||
RAISING lcx_2fa_comm_error,
|
||||
"! Delete all previously created access tokens for abapGit
|
||||
"! @parameter iv_url | Repository url
|
||||
"! @parameter iv_username | Username
|
||||
"! @parameter iv_password | Password
|
||||
"! @parameter iv_2fa_token | Two factor token
|
||||
"! @raising lcx_2fa_token_del_failed | Token deletion failed
|
||||
"! @raising lcx_2fa_auth_failed | Authentication failed
|
||||
delete_access_tokens IMPORTING iv_url TYPE string
|
||||
iv_username TYPE string
|
||||
iv_password TYPE string
|
||||
iv_2fa_token TYPE string
|
||||
RAISING lcx_2fa_del_failed
|
||||
lcx_2fa_comm_error
|
||||
lcx_2fa_auth_failed,
|
||||
"! Begin an authenticator session that uses internal caching for authorizations
|
||||
"! @raising lcx_2fa_illegal_state | Session already started
|
||||
begin RAISING lcx_2fa_illegal_state,
|
||||
"! End an authenticator session and clear internal caches
|
||||
"! @raising lcx_2fa_illegal_state | Session not running
|
||||
end RAISING lcx_2fa_illegal_state.
|
||||
ENDINTERFACE.
|
||||
|
||||
"! Default <em>LIF_2FA-AUTHENTICATOR</em> implememtation
|
||||
CLASS lcl_2fa_auth_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_2fa_required FOR lif_2fa_authenticator~is_2fa_required,
|
||||
delete_access_tokens FOR lif_2fa_authenticator~delete_access_tokens,
|
||||
begin FOR lif_2fa_authenticator~begin,
|
||||
end FOR lif_2fa_authenticator~end.
|
||||
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.
|
||||
CLASS-METHODS:
|
||||
"! 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_comm_error_from_sy RAISING lcx_2fa_comm_error.
|
||||
METHODS:
|
||||
"! @parameter rv_running | Internal session is currently active
|
||||
is_session_running RETURNING VALUE(rv_running) TYPE abap_bool.
|
||||
PRIVATE SECTION.
|
||||
DATA:
|
||||
mo_url_regex TYPE REF TO cl_abap_regex,
|
||||
mv_session_running TYPE abap_bool.
|
||||
ENDCLASS.
|
||||
|
||||
CLASS lcl_2fa_auth_base IMPLEMENTATION.
|
||||
METHOD constructor.
|
||||
CREATE OBJECT mo_url_regex
|
||||
EXPORTING
|
||||
pattern = iv_supported_url_regex
|
||||
ignore_case = abap_true.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD authenticate.
|
||||
RAISE EXCEPTION TYPE lcx_2fa_auth_failed. " Needs to be overwritten in subclasses
|
||||
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_2fa_required.
|
||||
rv_required = abap_false.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD delete_access_tokens.
|
||||
RAISE EXCEPTION TYPE lcx_2fa_del_failed. " Needs to be overwritten in subclasses
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD raise_comm_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_comm_error
|
||||
EXPORTING
|
||||
iv_error_text = |Communication error: { lv_error_msg }| ##NO_TEXT.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD begin.
|
||||
IF mv_session_running = abap_true.
|
||||
RAISE EXCEPTION TYPE lcx_2fa_illegal_state.
|
||||
ENDIF.
|
||||
|
||||
mv_session_running = abap_true.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD end.
|
||||
IF mv_session_running = abap_false.
|
||||
RAISE EXCEPTION TYPE lcx_2fa_illegal_state.
|
||||
ENDIF.
|
||||
|
||||
mv_session_running = abap_false.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD is_session_running.
|
||||
rv_running = mv_session_running.
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
|
||||
CLASS lcl_2fa_github_auth DEFINITION INHERITING FROM lcl_2fa_auth_base
|
||||
FINAL CREATE PUBLIC.
|
||||
|
||||
PUBLIC SECTION.
|
||||
METHODS:
|
||||
constructor,
|
||||
get_service_id_from_url REDEFINITION,
|
||||
authenticate REDEFINITION,
|
||||
is_2fa_required REDEFINITION,
|
||||
delete_access_tokens REDEFINITION,
|
||||
end REDEFINITION.
|
||||
PROTECTED SECTION.
|
||||
PRIVATE SECTION.
|
||||
CONSTANTS:
|
||||
gc_github_api_url TYPE string VALUE `https://api.github.com/`,
|
||||
gc_otp_header_name TYPE string VALUE `X-Github-OTP`,
|
||||
gc_restendpoint_authorizations TYPE string VALUE `/authorizations`.
|
||||
CLASS-METHODS:
|
||||
set_new_token_request IMPORTING ii_request TYPE REF TO if_http_request,
|
||||
get_token_from_response IMPORTING ii_response TYPE REF TO if_http_response
|
||||
RETURNING VALUE(rv_token) TYPE string,
|
||||
set_list_token_request IMPORTING ii_request TYPE REF TO if_http_request,
|
||||
get_tobedel_tokens_from_resp IMPORTING ii_response TYPE REF TO if_http_response
|
||||
RETURNING VALUE(rt_ids) TYPE stringtab,
|
||||
set_del_token_request IMPORTING ii_request TYPE REF TO if_http_request
|
||||
iv_token_id TYPE string.
|
||||
METHODS:
|
||||
get_authenticated_client IMPORTING iv_username TYPE string
|
||||
iv_password TYPE string
|
||||
iv_2fa_token TYPE string
|
||||
RETURNING VALUE(ri_client) TYPE REF TO if_http_client
|
||||
RAISING lcx_2fa_auth_failed
|
||||
lcx_2fa_comm_error.
|
||||
DATA:
|
||||
mi_authenticated_session TYPE REF TO if_http_client.
|
||||
ENDCLASS.
|
||||
|
||||
CLASS lcl_2fa_github_auth IMPLEMENTATION.
|
||||
METHOD constructor.
|
||||
super->constructor( '^https?://(www\.)?github.com.*$' ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD authenticate.
|
||||
DATA: li_http_client TYPE REF TO if_http_client,
|
||||
lv_http_code TYPE i,
|
||||
lv_http_code_description TYPE string.
|
||||
|
||||
" 1. Try to login to GitHub API
|
||||
li_http_client = get_authenticated_client( iv_username = iv_username
|
||||
iv_password = iv_password
|
||||
iv_2fa_token = iv_2fa_token ).
|
||||
|
||||
" 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_new_token_request( ii_request = li_http_client->request ).
|
||||
|
||||
li_http_client->send( EXCEPTIONS OTHERS = 1 ).
|
||||
IF sy-subrc <> 0.
|
||||
raise_comm_error_from_sy( ).
|
||||
ENDIF.
|
||||
|
||||
li_http_client->receive( EXCEPTIONS OTHERS = 1 ).
|
||||
IF sy-subrc <> 0.
|
||||
raise_comm_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_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_gen_failed
|
||||
EXPORTING
|
||||
iv_error_text = 'Token generation failed: parser error' ##NO_TEXT.
|
||||
ENDIF.
|
||||
|
||||
" GitHub might need some time until the new token is ready to use, give it a second
|
||||
CALL FUNCTION 'RZL_SLEEP'.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD set_new_token_request.
|
||||
DATA: lv_json_string TYPE string.
|
||||
|
||||
lv_json_string = `{"scopes":["repo"],"note":"Generated by abapGit","fingerprint":"abapGit2FA"}`.
|
||||
|
||||
ii_request->set_data( cl_abap_codepage=>convert_to( lv_json_string ) ).
|
||||
ii_request->set_header_field( name = if_http_header_fields_sap=>request_uri
|
||||
value = gc_restendpoint_authorizations ).
|
||||
ii_request->set_method( if_http_request=>co_request_method_post ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD set_list_token_request.
|
||||
ii_request->set_header_field( name = if_http_header_fields_sap=>request_uri
|
||||
value = gc_restendpoint_authorizations ).
|
||||
ii_request->set_method( if_http_request=>co_request_method_get ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD set_del_token_request.
|
||||
DATA: lv_url TYPE string.
|
||||
|
||||
lv_url = |{ gc_restendpoint_authorizations }/{ iv_token_id }|.
|
||||
|
||||
ii_request->set_header_field( name = if_http_header_fields_sap=>request_uri
|
||||
value = lv_url ).
|
||||
" Other methods than POST and GET do not have constants unfortunately
|
||||
" ii_request->set_method( if_http_request=>co_request_method_delete ).
|
||||
ii_request->set_method( 'DELETE' ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD get_token_from_response.
|
||||
CONSTANTS: lc_search_regex TYPE string VALUE `.*"token":"([^"]*).*$`.
|
||||
DATA: lv_response TYPE string,
|
||||
lo_regex TYPE REF TO cl_abap_regex,
|
||||
lo_matcher TYPE REF TO cl_abap_matcher.
|
||||
|
||||
lv_response = cl_abap_codepage=>convert_from( ii_response->get_data( ) ).
|
||||
|
||||
CREATE OBJECT lo_regex
|
||||
EXPORTING
|
||||
pattern = lc_search_regex.
|
||||
|
||||
lo_matcher = lo_regex->create_matcher( text = lv_response ).
|
||||
IF lo_matcher->match( ) = abap_true.
|
||||
rv_token = lo_matcher->get_submatch( 1 ).
|
||||
ENDIF.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD get_tobedel_tokens_from_resp.
|
||||
CONSTANTS: lc_search_regex TYPE string
|
||||
VALUE `\{"id": ?(\d+)[^\{]*"app":\{[^\{^\}]*\}[^\{]*"fingerprint": ?` &
|
||||
`"abapGit2FA"[^\{]*\}`.
|
||||
DATA: lv_response TYPE string,
|
||||
lo_regex TYPE REF TO cl_abap_regex,
|
||||
lo_matcher TYPE REF TO cl_abap_matcher.
|
||||
|
||||
lv_response = cl_abap_codepage=>convert_from( ii_response->get_data( ) ).
|
||||
|
||||
CREATE OBJECT lo_regex
|
||||
EXPORTING
|
||||
pattern = lc_search_regex.
|
||||
|
||||
lo_matcher = lo_regex->create_matcher( text = lv_response ).
|
||||
WHILE lo_matcher->find_next( ) = abap_true.
|
||||
APPEND lo_matcher->get_submatch( 1 ) TO rt_ids.
|
||||
ENDWHILE.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD get_service_id_from_url.
|
||||
rv_id = 'github'.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD is_2fa_required.
|
||||
|
||||
DATA: li_client TYPE REF TO if_http_client,
|
||||
lo_proxy TYPE REF TO zcl_abapgit_proxy_config.
|
||||
|
||||
|
||||
CREATE OBJECT lo_proxy.
|
||||
|
||||
cl_http_client=>create_by_url(
|
||||
EXPORTING
|
||||
url = gc_github_api_url
|
||||
ssl_id = 'ANONYM'
|
||||
proxy_host = lo_proxy->get_proxy_url( )
|
||||
proxy_service = lo_proxy->get_proxy_port( )
|
||||
IMPORTING
|
||||
client = li_client
|
||||
EXCEPTIONS
|
||||
argument_not_found = 1
|
||||
plugin_not_active = 2
|
||||
internal_error = 3
|
||||
OTHERS = 4 ).
|
||||
IF sy-subrc <> 0.
|
||||
raise_comm_error_from_sy( ).
|
||||
ENDIF.
|
||||
|
||||
li_client->propertytype_logon_popup = if_http_client=>co_disabled.
|
||||
|
||||
" The request needs to use something other than GET and it needs to be send to an endpoint
|
||||
" to trigger a SMS.
|
||||
li_client->request->set_header_field( name = if_http_header_fields_sap=>request_uri
|
||||
value = gc_restendpoint_authorizations ).
|
||||
li_client->request->set_method( if_http_request=>co_request_method_post ).
|
||||
|
||||
" Try to authenticate, if 2FA is required there will be a specific response header
|
||||
li_client->authenticate( username = iv_username password = iv_password ).
|
||||
|
||||
li_client->send( EXCEPTIONS OTHERS = 1 ).
|
||||
IF sy-subrc <> 0.
|
||||
raise_comm_error_from_sy( ).
|
||||
ENDIF.
|
||||
|
||||
li_client->receive( EXCEPTIONS OTHERS = 1 ).
|
||||
IF sy-subrc <> 0.
|
||||
raise_comm_error_from_sy( ).
|
||||
ENDIF.
|
||||
|
||||
" The response will either be UNAUTHORIZED or MALFORMED which is both fine.
|
||||
|
||||
IF li_client->response->get_header_field( gc_otp_header_name ) CP 'required*'.
|
||||
rv_required = abap_true.
|
||||
ENDIF.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD delete_access_tokens.
|
||||
DATA: li_http_client TYPE REF TO if_http_client,
|
||||
lv_http_code TYPE i,
|
||||
lv_http_code_description TYPE string,
|
||||
lt_tobedeleted_tokens TYPE stringtab.
|
||||
FIELD-SYMBOLS: <lv_id> TYPE string.
|
||||
|
||||
li_http_client = get_authenticated_client( iv_username = iv_username
|
||||
iv_password = iv_password
|
||||
iv_2fa_token = iv_2fa_token ).
|
||||
|
||||
set_list_token_request( li_http_client->request ).
|
||||
li_http_client->send( EXCEPTIONS OTHERS = 1 ).
|
||||
IF sy-subrc <> 0.
|
||||
raise_comm_error_from_sy( ).
|
||||
ENDIF.
|
||||
|
||||
li_http_client->receive( EXCEPTIONS OTHERS = 1 ).
|
||||
IF sy-subrc <> 0.
|
||||
raise_comm_error_from_sy( ).
|
||||
ENDIF.
|
||||
|
||||
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_del_failed
|
||||
EXPORTING
|
||||
iv_error_text = |Could not fetch current 2FA authorizations: | &&
|
||||
|{ lv_http_code } { lv_http_code_description }|.
|
||||
ENDIF.
|
||||
|
||||
lt_tobedeleted_tokens = get_tobedel_tokens_from_resp( li_http_client->response ).
|
||||
LOOP AT lt_tobedeleted_tokens ASSIGNING <lv_id> WHERE table_line IS NOT INITIAL.
|
||||
set_del_token_request( ii_request = li_http_client->request
|
||||
iv_token_id = <lv_id> ).
|
||||
li_http_client->send( EXCEPTIONS OTHERS = 1 ).
|
||||
IF sy-subrc <> 0.
|
||||
raise_comm_error_from_sy( ).
|
||||
ENDIF.
|
||||
|
||||
li_http_client->receive( EXCEPTIONS OTHERS = 1 ).
|
||||
IF sy-subrc <> 0.
|
||||
raise_comm_error_from_sy( ).
|
||||
ENDIF.
|
||||
|
||||
li_http_client->response->get_status(
|
||||
IMPORTING
|
||||
code = lv_http_code
|
||||
reason = lv_http_code_description ).
|
||||
IF lv_http_code <> 204.
|
||||
RAISE EXCEPTION TYPE lcx_2fa_del_failed
|
||||
EXPORTING
|
||||
iv_error_text = |Could not delete token '{ <lv_id> }': | &&
|
||||
|{ lv_http_code } { lv_http_code_description }|.
|
||||
ENDIF.
|
||||
ENDLOOP.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD get_authenticated_client.
|
||||
DATA: lv_http_code TYPE i,
|
||||
lv_http_code_description TYPE string,
|
||||
lo_settings TYPE REF TO zcl_abapgit_settings.
|
||||
|
||||
" If there is a cached client return it instead
|
||||
IF is_session_running( ) = abap_true AND mi_authenticated_session IS BOUND.
|
||||
ri_client = mi_authenticated_session.
|
||||
RETURN.
|
||||
ENDIF.
|
||||
|
||||
" Try to login to GitHub API with username, password and 2fa token
|
||||
|
||||
lo_settings = zcl_abapgit_persist_settings=>get_instance( )->read( ).
|
||||
|
||||
cl_http_client=>create_by_url(
|
||||
EXPORTING
|
||||
url = gc_github_api_url
|
||||
ssl_id = 'ANONYM'
|
||||
proxy_host = lo_settings->get_proxy_url( )
|
||||
proxy_service = lo_settings->get_proxy_port( )
|
||||
IMPORTING
|
||||
client = ri_client
|
||||
EXCEPTIONS
|
||||
argument_not_found = 1
|
||||
plugin_not_active = 2
|
||||
internal_error = 3
|
||||
OTHERS = 4 ).
|
||||
IF sy-subrc <> 0.
|
||||
raise_comm_error_from_sy( ).
|
||||
ENDIF.
|
||||
|
||||
" https://developer.github.com/v3/auth/#working-with-two-factor-authentication
|
||||
ri_client->propertytype_accept_cookie = if_http_client=>co_enabled.
|
||||
ri_client->request->set_header_field( name = gc_otp_header_name value = iv_2fa_token ).
|
||||
ri_client->authenticate( username = iv_username password = iv_password ).
|
||||
ri_client->propertytype_logon_popup = if_http_client=>co_disabled.
|
||||
|
||||
ri_client->send( EXCEPTIONS OTHERS = 1 ).
|
||||
IF sy-subrc <> 0.
|
||||
raise_comm_error_from_sy( ).
|
||||
ENDIF.
|
||||
|
||||
ri_client->receive( EXCEPTIONS OTHERS = 1 ).
|
||||
IF sy-subrc <> 0.
|
||||
raise_comm_error_from_sy( ).
|
||||
ENDIF.
|
||||
|
||||
" Check if authentication has succeeded
|
||||
ri_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.
|
||||
|
||||
" Cache the authenticated http session / client to avoid unnecessary additional authentication
|
||||
IF is_session_running( ) = abap_true.
|
||||
mi_authenticated_session = ri_client.
|
||||
ENDIF.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD end.
|
||||
super->end( ).
|
||||
FREE mi_authenticated_session.
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
|
||||
"! Static registry class to find <em>LIF_2FA_AUTHENTICATOR</em> instances
|
||||
CLASS lcl_2fa_auth_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,
|
||||
"! Offer to use two factor authentication if supported and required
|
||||
"! <p>
|
||||
"! This uses GUI functionality to display a popup to request the user to enter a two factor
|
||||
"! token. Also an dummy authentication request might be used to find out if two factor
|
||||
"! authentication is required for the account.
|
||||
"! </p>
|
||||
"! @parameter iv_url | Url of the repository / service
|
||||
"! @parameter cv_username | Username
|
||||
"! @parameter cv_password | Password, will be replaced by an access token if two factor
|
||||
"! authentication succeeds
|
||||
"! @raising zcx_abapgit_exception | Error in two factor authentication
|
||||
use_2fa_if_required IMPORTING iv_url TYPE string
|
||||
CHANGING cv_username TYPE string
|
||||
cv_password TYPE string
|
||||
RAISING zcx_abapgit_exception.
|
||||
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.
|
||||
PRIVATE SECTION.
|
||||
CLASS-METHODS:
|
||||
popup_token
|
||||
RETURNING VALUE(rv_token) TYPE string
|
||||
RAISING zcx_abapgit_exception.
|
||||
ENDCLASS.
|
||||
|
||||
CLASS lcl_2fa_auth_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_auth.
|
||||
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.
|
||||
|
||||
METHOD use_2fa_if_required.
|
||||
DATA: li_authenticator TYPE REF TO lif_2fa_authenticator,
|
||||
lv_2fa_token TYPE string,
|
||||
lv_access_token TYPE string,
|
||||
lx_ex TYPE REF TO cx_root.
|
||||
|
||||
IF is_url_supported( iv_url ) = abap_false.
|
||||
RETURN.
|
||||
ENDIF.
|
||||
|
||||
TRY.
|
||||
li_authenticator = get_authenticator_for_url( iv_url ).
|
||||
li_authenticator->begin( ).
|
||||
|
||||
" Is two factor authentication required for this account?
|
||||
IF li_authenticator->is_2fa_required( iv_url = iv_url
|
||||
iv_username = cv_username
|
||||
iv_password = cv_password ) = abap_true.
|
||||
|
||||
lv_2fa_token = popup_token( ).
|
||||
|
||||
" Delete an old access token if it exists
|
||||
li_authenticator->delete_access_tokens( iv_url = iv_url
|
||||
iv_username = cv_username
|
||||
iv_password = cv_password
|
||||
iv_2fa_token = lv_2fa_token ).
|
||||
|
||||
" Get a new access token
|
||||
lv_access_token = li_authenticator->authenticate( iv_url = iv_url
|
||||
iv_username = cv_username
|
||||
iv_password = cv_password
|
||||
iv_2fa_token = lv_2fa_token ).
|
||||
|
||||
" Use the access token instead of the password
|
||||
cv_password = lv_access_token.
|
||||
ENDIF.
|
||||
|
||||
li_authenticator->end( ).
|
||||
|
||||
CATCH lcx_2fa_error INTO lx_ex.
|
||||
TRY.
|
||||
li_authenticator->end( ).
|
||||
CATCH lcx_2fa_illegal_state ##NO_HANDLER.
|
||||
ENDTRY.
|
||||
|
||||
zcx_abapgit_exception=>raise( |2FA error: { lx_ex->get_text( ) }| ).
|
||||
ENDTRY.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD popup_token.
|
||||
|
||||
DATA: lv_returncode TYPE c,
|
||||
lt_fields TYPE TABLE OF sval.
|
||||
|
||||
FIELD-SYMBOLS: <ls_field> LIKE LINE OF lt_fields.
|
||||
|
||||
|
||||
APPEND INITIAL LINE TO lt_fields ASSIGNING <ls_field>.
|
||||
<ls_field>-tabname = 'TADIR'.
|
||||
<ls_field>-fieldname = 'OBJ_NAME'.
|
||||
<ls_field>-fieldtext = 'Two factor auth. token'.
|
||||
|
||||
CALL FUNCTION 'POPUP_GET_VALUES'
|
||||
EXPORTING
|
||||
no_value_check = abap_true
|
||||
popup_title = 'Two factor auth. token'
|
||||
IMPORTING
|
||||
returncode = lv_returncode
|
||||
TABLES
|
||||
fields = lt_fields
|
||||
EXCEPTIONS
|
||||
error_in_fields = 1
|
||||
OTHERS = 2. "#EC NOTEXT
|
||||
IF sy-subrc <> 0.
|
||||
zcx_abapgit_exception=>raise( 'Error from POPUP_GET_VALUES' ).
|
||||
ENDIF.
|
||||
|
||||
IF lv_returncode = 'A'.
|
||||
zcx_abapgit_exception=>raise( 'Authentication cancelled' ).
|
||||
ENDIF.
|
||||
|
||||
READ TABLE lt_fields INDEX 1 ASSIGNING <ls_field>.
|
||||
ASSERT sy-subrc = 0.
|
||||
rv_token = <ls_field>-value.
|
||||
|
||||
ENDMETHOD.
|
||||
|
||||
ENDCLASS.
|
||||
* todo, include to be deleted
|
||||
|
|
|
@ -2,54 +2,6 @@
|
|||
*& Include ZABAPGIT_HTTP
|
||||
*&---------------------------------------------------------------------*
|
||||
|
||||
CLASS lcl_proxy_auth DEFINITION FINAL.
|
||||
|
||||
PUBLIC SECTION.
|
||||
CLASS-METHODS:
|
||||
run
|
||||
IMPORTING ii_client TYPE REF TO if_http_client
|
||||
RAISING zcx_abapgit_exception.
|
||||
|
||||
PRIVATE SECTION.
|
||||
CLASS-DATA: gv_username TYPE string,
|
||||
gv_password TYPE string.
|
||||
|
||||
CLASS-METHODS: enter RAISING zcx_abapgit_exception.
|
||||
|
||||
ENDCLASS.
|
||||
|
||||
CLASS lcl_proxy_auth IMPLEMENTATION.
|
||||
|
||||
METHOD run.
|
||||
|
||||
IF gv_username IS INITIAL OR gv_password IS INITIAL.
|
||||
enter( ).
|
||||
ENDIF.
|
||||
|
||||
ii_client->authenticate(
|
||||
proxy_authentication = abap_true
|
||||
username = gv_username
|
||||
password = gv_password ).
|
||||
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD enter.
|
||||
|
||||
zcl_abapgit_password_dialog=>popup(
|
||||
EXPORTING
|
||||
iv_repo_url = 'Proxy Authentication'
|
||||
CHANGING
|
||||
cv_user = gv_username
|
||||
cv_pass = gv_password ).
|
||||
|
||||
IF gv_username IS INITIAL OR gv_password IS INITIAL.
|
||||
zcx_abapgit_exception=>raise( 'Proxy auth failed' ).
|
||||
ENDIF.
|
||||
|
||||
ENDMETHOD.
|
||||
|
||||
ENDCLASS.
|
||||
|
||||
CLASS lcl_http DEFINITION FINAL.
|
||||
|
||||
PUBLIC SECTION.
|
||||
|
@ -131,7 +83,7 @@ CLASS lcl_http IMPLEMENTATION.
|
|||
ENDIF.
|
||||
|
||||
IF lo_proxy_configuration->get_proxy_authentication( iv_url ) = abap_true.
|
||||
lcl_proxy_auth=>run( li_client ).
|
||||
zcl_abapgit_proxy_auth=>run( li_client ).
|
||||
ENDIF.
|
||||
|
||||
CREATE OBJECT ro_client
|
||||
|
@ -255,7 +207,7 @@ CLASS lcl_http IMPLEMENTATION.
|
|||
ENDIF.
|
||||
|
||||
" Offer two factor authentication if it is available and required
|
||||
lcl_2fa_auth_registry=>use_2fa_if_required(
|
||||
zcl_abapgit_2fa_auth_registry=>use_2fa_if_required(
|
||||
EXPORTING
|
||||
iv_url = iv_url
|
||||
CHANGING
|
||||
|
|
Loading…
Reference in New Issue
Block a user