Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/v4-api' into v4-api-resources-po…
Browse files Browse the repository at this point in the history
…sts-products
  • Loading branch information
n7studios committed Apr 30, 2024
2 parents 0ec60a1 + 7aa6e9f commit f8fe5e6
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 19 deletions.
96 changes: 84 additions & 12 deletions src/class-convertkit-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -599,8 +599,6 @@ public function unsubscribe_by_email( string $email_address ) {
*/
public function get_all_posts( $posts_per_request = 50 ) {

$this->log( 'API: get_all_posts()' );

// Sanitize some parameters.
$posts_per_request = absint( $posts_per_request );

Expand Down Expand Up @@ -663,8 +661,6 @@ public function get_all_posts( $posts_per_request = 50 ) {
*/
public function get_posts( $page = 1, $per_page = 10 ) {

$this->log( 'API: get_posts()' );

// Sanitize some parameters.
$page = absint( $page );
$per_page = absint( $per_page );
Expand Down Expand Up @@ -723,8 +719,6 @@ public function get_posts( $page = 1, $per_page = 10 ) {
*/
public function get_post( $post_id ) {

$this->log( 'API: get_post(): [ post_id: ' . $post_id . ']' );

// Send request.
$response = $this->get(
sprintf( 'posts/%s', $post_id ),
Expand Down Expand Up @@ -808,8 +802,6 @@ public function get_products() {
*/
public function subscriber_authentication_send_code( $email, $redirect_url ) {

$this->log( 'API: subscriber_authentication_send_code(): [ email: ' . $email . ', redirect_url: ' . $redirect_url . ']' );

// Sanitize some parameters.
$email = trim( $email );
$redirect_url = trim( $redirect_url );
Expand Down Expand Up @@ -865,8 +857,6 @@ public function subscriber_authentication_send_code( $email, $redirect_url ) {
*/
public function subscriber_authentication_verify( $token, $subscriber_code ) {

$this->log( 'API: subscriber_authentication_verify(): [ token: ' . $this->mask_string( $token ) . ', subscriber_code: ' . $this->mask_string( $subscriber_code ) . ']' );

// Sanitize some parameters.
$token = trim( $token );
$subscriber_code = trim( $subscriber_code );
Expand Down Expand Up @@ -917,8 +907,6 @@ public function subscriber_authentication_verify( $token, $subscriber_code ) {
*/
public function profile( $signed_subscriber_id ) {

$this->log( 'API: profile(): [ signed_subscriber_id: ' . $this->mask_string( $signed_subscriber_id ) . ' ]' );

// Trim some parameters.
$signed_subscriber_id = trim( $signed_subscriber_id );

Expand Down Expand Up @@ -1188,6 +1176,21 @@ private function is_json( $json_string ) {
*/
public function request( $endpoint, $method = 'get', $params = array(), $retry_if_rate_limit_hit = true ) {

// Log request.
$log = sprintf(
'API: %s %s',
$method,
$this->mask_endpoint( $endpoint )
);
if ( count( $params ) ) {
$log = sprintf(
'%s: %s',
$log,
wp_json_encode( $this->mask_params( $params ) )
);
}
$this->log( $log );

// Send request.
switch ( strtolower( $method ) ) {
case 'get':
Expand Down Expand Up @@ -1303,6 +1306,8 @@ public function request( $endpoint, $method = 'get', $params = array(), $retry_i
break;
}

$this->log( 'API: Error: ' . $error );

return new WP_Error(
'convertkit_api_error',
$error,
Expand Down Expand Up @@ -1564,6 +1569,73 @@ private function get_error_message( $key ) {

}

/**
* Helper method to mask the given endpoint URL where it contains
* sensitive data, such as a signed subscriber ID.
*
* @since 2.0.0
*
* @param string $endpoint String to mask.
* @return string Masked string
*/
private function mask_endpoint( $endpoint ) {

// Endpoints to mask string.
$keys = array(
'profile/',
);

foreach ( $keys as $key => $value ) {
// Skip if the endpoint isn't one we need to mask.
if ( strpos( $endpoint, $value ) === false ) {
continue;
}

// Mask after the key e.g. profile/******abcd.
return $value . $this->mask_string( str_replace( $value, '', $endpoint ) );

}

// Just return the endpoint, as it doesn't need masking.
return $endpoint;

}

/**
* Helper method to mask values for specific keys in an array.
*
* @since 2.0.0
*
* @param array $params Array of parameters to mask.
* @return array Array of parameters with masked values where applicable
*/
private function mask_params( $params ) {

// Keys to mask values for.
$keys = array(
'first_name',
'token',
'subscriber_code',
);

foreach ( $params as $key => $value ) {
// Skip if not a string.
if ( ! is_string( $value ) ) {
continue;
}

// Skip if the key isn't one we need to mask the value of.
if ( ! in_array( $key, $keys, true ) ) {
continue;
}

$params[ $key ] = $this->mask_string( $value );
}

return $params;

}

/**
* Helper method to mask all but the last 4 characters of a string.
*
Expand Down
4 changes: 3 additions & 1 deletion tests/wpunit/APINoDataTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,9 @@ public function testGetAllPostsNoData()
public function testGetProducts()
{
$result = $this->api->get_products();
$this->assertNoData($result, 'products');
$this->assertNotInstanceOf(WP_Error::class, $result);
$this->assertIsArray($result);
$this->assertCount(0, $result);
}

/**
Expand Down
16 changes: 10 additions & 6 deletions tests/wpunit/APITest.php
Original file line number Diff line number Diff line change
Expand Up @@ -135,16 +135,20 @@ public function tearDown(): void
*/
public function testLog()
{
$this->markTestIncomplete();

// Define location for log file.
define( 'CONVERTKIT_PLUGIN_PATH', $_ENV['WP_ROOT_FOLDER'] . '/wp-content/uploads' );

// Create a log.txt file.
$this->tester->writeToFile(CONVERTKIT_PLUGIN_PATH . '/log.txt', 'historical log file');

// Initialize API.
$api = new ConvertKit_API( $_ENV['CONVERTKIT_API_KEY'], $_ENV['CONVERTKIT_API_SECRET'], true );
// Initialize API with logging enabled.
$api = new ConvertKit_API(
$_ENV['CONVERTKIT_OAUTH_CLIENT_ID'],
$_ENV['CONVERTKIT_OAUTH_REDIRECT_URI'],
$_ENV['CONVERTKIT_OAUTH_ACCESS_TOKEN'],
$_ENV['CONVERTKIT_OAUTH_REFRESH_TOKEN'],
true
);

// Perform actions that will write sensitive data to the log file.
$api->form_subscribe(
Expand All @@ -168,8 +172,8 @@ public function testLog()

// Confirm the contents of the log file have masked the email address, name and signed subscriber ID.
$this->tester->openFile(CONVERTKIT_PLUGIN_PATH . '/log/log.txt');
$this->tester->seeInThisFile('API: form_subscribe(): [ form_id: ' . $_ENV['CONVERTKIT_API_FORM_ID'] . ', email: o****@n********.c**, first_name: ******Name ]');
$this->tester->seeInThisFile('API: profile(): [ signed_subscriber_id: ********************');
$this->tester->seeInThisFile('API: POST subscribers: {"email_address":"o****@n********.c**","first_name":"******Name","state":"active","fields":{"last_name":"Last"}}');
$this->tester->seeInThisFile('API: GET profile/*****************************************');
$this->tester->dontSeeInThisFile($_ENV['CONVERTKIT_API_SUBSCRIBER_EMAIL']);
$this->tester->dontSeeInThisFile('First Name');
$this->tester->dontSeeInThisFile($_ENV['CONVERTKIT_API_SIGNED_SUBSCRIBER_ID']);
Expand Down

0 comments on commit f8fe5e6

Please sign in to comment.