|
| 1 | +REPORT z_strust_installer LINE-SIZE 255. |
| 2 | + |
| 3 | +************************************************************************ |
| 4 | +* Trust Management: Certificate Installer |
| 5 | +* |
| 6 | +* Copyright 2025 apm.to Inc. <https://apm.to> |
| 7 | +* SPDX-License-Identifier: MIT |
| 8 | +************************************************************************ |
| 9 | + |
| 10 | +SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE TEXT-t01. |
| 11 | + PARAMETERS: |
| 12 | + p_cont TYPE psecontext DEFAULT 'SSLC' OBLIGATORY, |
| 13 | + p_appl TYPE ssfappl DEFAULT 'ANONYM' OBLIGATORY. |
| 14 | +SELECTION-SCREEN END OF BLOCK b1. |
| 15 | + |
| 16 | +SELECTION-SCREEN BEGIN OF BLOCK b2 WITH FRAME TITLE TEXT-t02. |
| 17 | + PARAMETERS p_domain TYPE string OBLIGATORY LOWER CASE. |
| 18 | +SELECTION-SCREEN END OF BLOCK b2. |
| 19 | + |
| 20 | +SELECTION-SCREEN BEGIN OF BLOCK b3 WITH FRAME TITLE TEXT-t03. |
| 21 | + PARAMETERS: |
| 22 | + p_passwd TYPE string LOWER CASE, |
| 23 | + p_root AS CHECKBOX DEFAULT abap_false, |
| 24 | + p_test AS CHECKBOX DEFAULT abap_true. |
| 25 | +SELECTION-SCREEN END OF BLOCK b3. |
| 26 | + |
| 27 | +CONSTANTS c_api TYPE string VALUE 'https://tools.abappm.com/api/v1/certificates'. |
| 28 | + |
| 29 | +INITIALIZATION. |
| 30 | + |
| 31 | + DATA(subrc) = cl_abap_pse=>authority_check( iv_activity = '01' ) |
| 32 | + + cl_abap_pse=>authority_check( iv_activity = '02' ) |
| 33 | + + cl_abap_pse=>authority_check( iv_activity = '06' ). |
| 34 | + IF subrc <> 0. |
| 35 | + MESSAGE 'You are not authorized to update certificates' TYPE 'E'. |
| 36 | + STOP. |
| 37 | + ENDIF. |
| 38 | + |
| 39 | +START-OF-SELECTION. |
| 40 | + |
| 41 | + CALL FUNCTION 'SSFPSE_PARAMETER' |
| 42 | + EXPORTING |
| 43 | + context = p_cont |
| 44 | + applic = p_appl |
| 45 | + EXCEPTIONS |
| 46 | + pse_not_found = 1 |
| 47 | + OTHERS = 2. |
| 48 | + IF sy-subrc <> 0. |
| 49 | + MESSAGE 'PSE not found' TYPE 'E'. |
| 50 | + STOP. |
| 51 | + ENDIF. |
| 52 | + |
| 53 | + TRY. |
| 54 | + DATA(strust) = zcl_strust2=>create( |
| 55 | + context = p_cont |
| 56 | + application = p_appl |
| 57 | + password = p_passwd ). |
| 58 | + CATCH zcx_error INTO DATA(error). |
| 59 | + MESSAGE error TYPE 'E'. |
| 60 | + STOP. |
| 61 | + ENDTRY. |
| 62 | + |
| 63 | + |
| 64 | + WRITE: /'Domain:', p_domain COLOR COL_POSITIVE. |
| 65 | + SKIP. |
| 66 | + |
| 67 | + " We can't query wildcard domains so we try with "api" sub-domain |
| 68 | + " TODO: if this fails, loop over a couple other common sub-domains |
| 69 | + DATA(query) = replace( val = p_domain sub = '*' with = 'api' ). |
| 70 | + query = cl_abap_dyn_prg=>escape_xss_url( query ). |
| 71 | + |
| 72 | + " Get certificate data from tools.abappm.com |
| 73 | + DATA(agent) = zcl_http_agent=>create( ). |
| 74 | + |
| 75 | + agent->global_headers( )->set( |
| 76 | + iv_key = zif_http_agent=>c_header-accept |
| 77 | + iv_val = zif_http_agent=>c_content_type-json ). |
| 78 | + |
| 79 | + DATA(response) = agent->request( |{ c_api }?domain={ query }| ). |
| 80 | + |
| 81 | + IF response->is_ok( ) = abap_false. |
| 82 | + WRITE: / 'Error getting certificates from API:' COLOR COL_NEGATIVE, response->error( ). |
| 83 | + STOP. |
| 84 | + ENDIF. |
| 85 | + |
| 86 | + TRY. |
| 87 | + DATA(ajson) = zcl_ajson=>parse( response->cdata( ) ). |
| 88 | + CATCH zcx_ajson_error INTO DATA(ajson_error). |
| 89 | + WRITE: / 'Error parsing API response:' COLOR COL_NEGATIVE, ajson_error->get_text( ). |
| 90 | + STOP. |
| 91 | + ENDTRY. |
| 92 | + |
| 93 | + IF ajson->get( '/error' ) IS NOT INITIAL. |
| 94 | + WRITE: / 'Error getting certificates from API:' COLOR COL_NEGATIVE, ajson->get( '/error' ). |
| 95 | + STOP. |
| 96 | + ENDIF. |
| 97 | + |
| 98 | + " Keep fingers crossed that the response matches what we need for the update |
| 99 | + DATA(cert_domain) = ajson->get( '/domain' ). |
| 100 | + |
| 101 | + IF cert_domain <> p_domain AND p_domain NA '*'. |
| 102 | + WRITE: / 'Certificates domain does not match request:' COLOR COL_TOTAL, cert_domain. |
| 103 | + STOP. |
| 104 | + ENDIF. |
| 105 | + |
| 106 | + " We finally have a certificate that can be used for the update, yay! |
| 107 | + TRY. |
| 108 | + " Root and intermediate certificates |
| 109 | + IF p_root = abap_true. |
| 110 | + |
| 111 | + LOOP AT ajson->members( '/intermediateCertificates' ) INTO DATA(member). |
| 112 | + |
| 113 | + DATA(inter_pem) = ajson->get( '/intermediateCertificates/' && member && '/pem' ). |
| 114 | + DATA(inter_date_from) = ajson->get( '/intermediateCertificates/' && member && '/validFrom' ). |
| 115 | + DATA(inter_date_to) = ajson->get( '/intermediateCertificates/' && member && '/validTo' ). |
| 116 | + DATA(inter_subject) = 'CN=' && ajson->get( '/intermediateCertificates/' && member && '/subject/CN' ). |
| 117 | + IF inter_subject = 'CN='. |
| 118 | + inter_subject = 'O=' && ajson->get( '/intermediateCertificates/' && member && '/subject/O' ). |
| 119 | + ENDIF. |
| 120 | + IF strlen( inter_subject ) > 78. |
| 121 | + inter_subject = inter_subject(75) && '...'. |
| 122 | + ENDIF. |
| 123 | + |
| 124 | + IF p_test = abap_false. |
| 125 | + strust->add_pem( inter_pem ). |
| 126 | + ENDIF. |
| 127 | + |
| 128 | + WRITE: /10 'Root/intermediate certificate added:' COLOR COL_POSITIVE, |
| 129 | + AT 50 inter_subject, |
| 130 | + AT 130 inter_date_from(10), |
| 131 | + AT 145 inter_date_to(10), |
| 132 | + AT 158 ''. |
| 133 | + |
| 134 | + ENDLOOP. |
| 135 | + SKIP. |
| 136 | + |
| 137 | + ENDIF. |
| 138 | + |
| 139 | + " Main certificate |
| 140 | + DATA(peer_pem) = ajson->get( '/peerCertificate/pem' ). |
| 141 | + DATA(peer_date_from) = ajson->get( '/peerCertificate/validFrom' ). |
| 142 | + DATA(peer_date_to) = ajson->get( '/peerCertificate/validTo' ). |
| 143 | + DATA(peer_subject) = 'CN=' && ajson->get( '/peerCertificate/subject/CN' ). |
| 144 | + IF peer_subject = 'CN='. |
| 145 | + peer_subject = 'O=' && ajson->get( '/peerCertificate/subject/O' ). |
| 146 | + ENDIF. |
| 147 | + IF strlen( peer_subject ) > 78. |
| 148 | + peer_subject = peer_subject(75) && '...'. |
| 149 | + ENDIF. |
| 150 | + |
| 151 | + IF p_test = abap_false. |
| 152 | + strust->add_pem( peer_pem ). |
| 153 | + ENDIF. |
| 154 | + |
| 155 | + WRITE: /10 'New certificate added:' COLOR COL_POSITIVE, |
| 156 | + AT 50 peer_subject, |
| 157 | + AT 130 peer_date_from(10), |
| 158 | + AT 145 peer_date_to(10), |
| 159 | + AT 158 ''. |
| 160 | + |
| 161 | + ULINE. |
| 162 | + |
| 163 | + IF p_test = abap_true. |
| 164 | + WRITE: / 'Test run' COLOR COL_TOTAL, '(changes were not saved)'. |
| 165 | + STOP. |
| 166 | + ENDIF. |
| 167 | + |
| 168 | + " Save changes |
| 169 | + strust->update( ). |
| 170 | + |
| 171 | + WRITE / 'Certificates saved' COLOR COL_POSITIVE. |
| 172 | + |
| 173 | + CATCH zcx_error INTO error. |
| 174 | + WRITE: / 'Error updating certificate:' COLOR COL_NEGATIVE, error->get_text( ). |
| 175 | + ENDTRY. |
0 commit comments