digest http authentication now works

yay
This commit is contained in:
larshp 2016-11-15 15:46:28 +00:00
parent 42a918609e
commit 866b5e5404

View File

@ -2,13 +2,61 @@
*& Include ZABAPGIT_HTTP *& Include ZABAPGIT_HTTP
*&---------------------------------------------------------------------* *&---------------------------------------------------------------------*
CLASS lcl_http_client DEFINITION FINAL. CLASS lcl_http_digest DEFINITION FINAL.
PUBLIC SECTION. PUBLIC SECTION.
METHODS:
constructor
IMPORTING
ii_client TYPE REF TO if_http_client
iv_username TYPE string
iv_password TYPE string,
run
IMPORTING
ii_client TYPE REF TO if_http_client.
PRIVATE SECTION.
DATA: mv_ha1 TYPE string,
mv_username TYPE string,
mv_realm TYPE string,
mv_qop TYPE string,
mv_nonce TYPE string.
CLASS-DATA: gv_nc TYPE n LENGTH 8.
CLASS-METHODS:
md5
IMPORTING
iv_data TYPE string
RETURNING
VALUE(rv_hash) TYPE string.
METHODS:
hash
IMPORTING
iv_qop TYPE string
iv_nonce TYPE string
iv_uri TYPE string
iv_method TYPE string
iv_cnonse TYPE string
RETURNING
VALUE(rv_response) TYPE string,
parse
IMPORTING
ii_client TYPE REF TO if_http_client.
ENDCLASS.
CLASS lcl_http_client DEFINITION FINAL.
PUBLIC SECTION.
METHODS: METHODS:
constructor constructor
IMPORTING ii_client TYPE REF TO if_http_client, IMPORTING ii_client TYPE REF TO if_http_client,
close, close,
set_digest
IMPORTING io_digest TYPE REF TO lcl_http_digest,
send_receive_close send_receive_close
IMPORTING IMPORTING
iv_data TYPE xstring iv_data TYPE xstring
@ -27,7 +75,8 @@ CLASS lcl_http_client DEFINITION FINAL.
RAISING lcx_exception. RAISING lcx_exception.
PROTECTED SECTION. PROTECTED SECTION.
DATA: mi_client TYPE REF TO if_http_client. DATA: mi_client TYPE REF TO if_http_client,
mo_digest TYPE REF TO lcl_http_digest.
ENDCLASS. ENDCLASS.
@ -37,6 +86,10 @@ CLASS lcl_http_client IMPLEMENTATION.
mi_client = ii_client. mi_client = ii_client.
ENDMETHOD. ENDMETHOD.
METHOD set_digest.
mo_digest = io_digest.
ENDMETHOD.
METHOD send_receive_close. METHOD send_receive_close.
* do not use set_cdata as it modifies the Content-Type header field * do not use set_cdata as it modifies the Content-Type header field
@ -85,6 +138,10 @@ CLASS lcl_http_client IMPLEMENTATION.
name = 'Accept' name = 'Accept'
value = lv_value ). "#EC NOTEXT value = lv_value ). "#EC NOTEXT
IF mo_digest IS BOUND.
mo_digest->run( mi_client ).
ENDIF.
ENDMETHOD. "set_headers ENDMETHOD. "set_headers
METHOD send_receive. METHOD send_receive.
@ -149,87 +206,44 @@ CLASS lcl_http_client IMPLEMENTATION.
ENDCLASS. ENDCLASS.
CLASS lcl_http_digest DEFINITION FINAL.
PUBLIC SECTION.
CLASS-METHODS:
run
IMPORTING
ii_client TYPE REF TO if_http_client
iv_username TYPE string
iv_password TYPE string.
PRIVATE SECTION.
CLASS-DATA: gv_nc TYPE n LENGTH 8.
CLASS-METHODS:
parse
IMPORTING
iv_value TYPE string
EXPORTING
ev_scheme TYPE string
ev_realm TYPE string
ev_qop TYPE string
ev_nonce TYPE string,
md5
IMPORTING
iv_data TYPE string
RETURNING
VALUE(rv_hash) TYPE string,
hash
IMPORTING
iv_qop TYPE string
iv_realm TYPE string
iv_nonce TYPE string
iv_username TYPE clike
iv_uri TYPE string
iv_method TYPE string
iv_cnonse TYPE string
iv_password TYPE clike
RETURNING
VALUE(rv_response) TYPE string.
ENDCLASS.
CLASS lcl_http_digest IMPLEMENTATION. CLASS lcl_http_digest IMPLEMENTATION.
METHOD constructor.
parse( ii_client ).
mv_ha1 = md5( |{ iv_username }:{ mv_realm }:{ iv_password }| ).
mv_username = iv_username.
ENDMETHOD.
METHOD hash. METHOD hash.
DATA(lv_ha1) = md5( |{ iv_username }:{ iv_realm }:{ iv_password }| ). DATA: lv_ha2 TYPE string.
DATA(lv_ha2) = md5( |{ iv_method }:{ iv_uri }| ).
rv_response = md5( |{ lv_ha1 }:{ iv_nonce }:{ gv_nc }:{ iv_cnonse }:{ iv_qop }:{ lv_ha2 }| ).
lv_ha2 = md5( |{ iv_method }:{ iv_uri }| ).
ASSERT NOT iv_cnonse IS INITIAL.
rv_response = md5( |{ mv_ha1 }:{ iv_nonce }:{ gv_nc }:{ iv_cnonse }:{ iv_qop }:{ lv_ha2 }| ).
ENDMETHOD. ENDMETHOD.
METHOD run. METHOD run.
DATA: lv_value TYPE string, DATA: lv_value TYPE string,
lv_scheme TYPE string,
lv_realm TYPE string,
lv_response TYPE string, lv_response TYPE string,
lv_method TYPE string, lv_method TYPE string,
lv_uri TYPE string,
lv_auth TYPE string,
lv_cnonce TYPE string, lv_cnonce TYPE string,
lv_qop TYPE string, lv_uri TYPE string,
lv_nonce TYPE string. lv_auth TYPE string.
lv_value = ii_client->response->get_header_field( 'www-authenticate' ). ASSERT NOT mv_nonce IS INITIAL.
parse( lv_method = ii_client->request->get_header_field( '~request_method' ).
EXPORTING
iv_value = lv_value
IMPORTING
ev_scheme = lv_scheme
ev_realm = lv_realm
ev_qop = lv_qop
ev_nonce = lv_nonce ).
ASSERT NOT lv_nonce IS INITIAL.
lv_method = 'GET'.
lv_uri = ii_client->request->get_header_field( '~request_uri' ). lv_uri = ii_client->request->get_header_field( '~request_uri' ).
CALL FUNCTION 'GENERAL_GET_RANDOM_STRING' CALL FUNCTION 'GENERAL_GET_RANDOM_STRING'
@ -239,21 +253,18 @@ CLASS lcl_http_digest IMPLEMENTATION.
random_string = lv_cnonce. random_string = lv_cnonce.
lv_response = hash( lv_response = hash(
iv_qop = lv_qop iv_qop = mv_qop
iv_realm = lv_realm iv_nonce = mv_nonce
iv_nonce = lv_nonce
iv_username = iv_username
iv_uri = lv_uri iv_uri = lv_uri
iv_method = lv_method iv_method = lv_method
iv_cnonse = lv_cnonce iv_cnonse = lv_cnonce ).
iv_password = iv_password ).
* client response * client response
lv_auth = |Digest username="{ iv_username lv_auth = |Digest username="{ mv_username
}", realm="{ lv_realm }", realm="{ mv_realm
}", nonce="{ lv_nonce }", nonce="{ mv_nonce
}", uri="{ lv_uri }", uri="{ lv_uri
}", qop={ lv_qop }", qop={ mv_qop
}, nc={ gv_nc }, nc={ gv_nc
}, cnonce="{ lv_cnonce }, cnonce="{ lv_cnonce
}", response="{ lv_response }"|. }", response="{ lv_response }"|.
@ -266,15 +277,14 @@ CLASS lcl_http_digest IMPLEMENTATION.
METHOD parse. METHOD parse.
CLEAR: ev_scheme, DATA: lv_value TYPE string.
ev_realm,
ev_qop,
ev_nonce.
FIND REGEX '^(\w+)' IN iv_value SUBMATCHES ev_scheme.
FIND REGEX 'realm="([\w ]+)"' IN iv_value SUBMATCHES ev_realm. lv_value = ii_client->response->get_header_field( 'www-authenticate' ).
FIND REGEX 'qop="(\w+)"' IN iv_value SUBMATCHES ev_qop.
FIND REGEX 'nonce="([\w=/+\$]+)"' IN iv_value SUBMATCHES ev_nonce. FIND REGEX 'realm="([\w ]+)"' IN lv_value SUBMATCHES mv_realm.
FIND REGEX 'qop="(\w+)"' IN lv_value SUBMATCHES mv_qop.
FIND REGEX 'nonce="([\w=/+\$]+)"' IN lv_value SUBMATCHES mv_nonce.
ENDMETHOD. ENDMETHOD.
@ -311,6 +321,10 @@ ENDCLASS.
CLASS lcl_http DEFINITION FINAL. CLASS lcl_http DEFINITION FINAL.
PUBLIC SECTION. PUBLIC SECTION.
CONSTANTS: BEGIN OF gc_scheme,
digest TYPE string VALUE 'Digest',
END OF gc_scheme.
CLASS-METHODS: CLASS-METHODS:
get_agent get_agent
RETURNING VALUE(rv_agent) TYPE string, RETURNING VALUE(rv_agent) TYPE string,
@ -331,7 +345,9 @@ CLASS lcl_http DEFINITION FINAL.
RETURNING VALUE(rv_bool) TYPE abap_bool, RETURNING VALUE(rv_bool) TYPE abap_bool,
acquire_login_details acquire_login_details
IMPORTING ii_client TYPE REF TO if_http_client IMPORTING ii_client TYPE REF TO if_http_client
io_client TYPE REF TO lcl_http_client
iv_url TYPE string iv_url TYPE string
RETURNING VALUE(rv_scheme) TYPE string
RAISING lcx_exception. RAISING lcx_exception.
ENDCLASS. ENDCLASS.
@ -348,7 +364,7 @@ CLASS lcl_http IMPLEMENTATION.
METHOD create_by_url. METHOD create_by_url.
DATA: lv_uri TYPE string, DATA: lv_uri TYPE string,
lv_expect_potentual_auth TYPE abap_bool, lv_scheme TYPE string,
li_client TYPE REF TO if_http_client, li_client TYPE REF TO if_http_client,
lo_settings TYPE REF TO lcl_settings. lo_settings TYPE REF TO lcl_settings.
@ -395,14 +411,17 @@ CLASS lcl_http IMPLEMENTATION.
ro_client->send_receive( ). ro_client->send_receive( ).
IF check_auth_requested( li_client ) = abap_true. IF check_auth_requested( li_client ) = abap_true.
acquire_login_details( ii_client = li_client lv_scheme = acquire_login_details( ii_client = li_client
io_client = ro_client
iv_url = iv_url ). iv_url = iv_url ).
ro_client->send_receive( ). ro_client->send_receive( ).
ENDIF. ENDIF.
ro_client->check_http_200( ). ro_client->check_http_200( ).
IF lv_scheme <> gc_scheme-digest.
lcl_login_manager=>save( iv_uri = iv_url lcl_login_manager=>save( iv_uri = iv_url
ii_client = li_client ). ii_client = li_client ).
ENDIF.
ENDMETHOD. ENDMETHOD.
@ -448,9 +467,9 @@ CLASS lcl_http IMPLEMENTATION.
METHOD acquire_login_details. METHOD acquire_login_details.
DATA: lv_default_user TYPE string, DATA: lv_default_user TYPE string,
lv_scheme TYPE string,
lv_user TYPE string, lv_user TYPE string,
lv_pass TYPE string. lv_pass TYPE string,
lo_digest TYPE REF TO lcl_http_digest.
lv_default_user = lcl_app=>user( )->get_repo_username( iv_url ). lv_default_user = lcl_app=>user( )->get_repo_username( iv_url ).
@ -472,17 +491,20 @@ CLASS lcl_http IMPLEMENTATION.
iv_username = lv_user ). iv_username = lv_user ).
ENDIF. ENDIF.
lv_scheme = ii_client->response->get_header_field( 'www-authenticate' ). rv_scheme = ii_client->response->get_header_field( 'www-authenticate' ).
FIND REGEX '^(\w+)' IN lv_scheme SUBMATCHES lv_scheme. FIND REGEX '^(\w+)' IN rv_scheme SUBMATCHES rv_scheme.
CASE lv_scheme. CASE rv_scheme.
WHEN 'Digest'. WHEN gc_scheme-digest.
* https://en.wikipedia.org/wiki/Digest_access_authentication * https://en.wikipedia.org/wiki/Digest_access_authentication
* eg used by https://www.gerritcodereview.com/ * e.g. used by https://www.gerritcodereview.com/
lcl_http_digest=>run( CREATE OBJECT lo_digest
EXPORTING
ii_client = ii_client ii_client = ii_client
iv_username = lv_user iv_username = lv_user
iv_password = lv_pass ). iv_password = lv_pass.
lo_digest->run( ii_client ).
io_client->set_digest( lo_digest ).
WHEN OTHERS. WHEN OTHERS.
* https://en.wikipedia.org/wiki/Basic_access_authentication * https://en.wikipedia.org/wiki/Basic_access_authentication
ii_client->authenticate( ii_client->authenticate(