Skip to content

Commit e3db367

Browse files
committed
Merge remote-tracking branch 'origin/v4-api-oauth' into v4-api-sequences
# Conflicts: # src/ConvertKit_API.php
2 parents 6814aec + 44ddbaa commit e3db367

File tree

3 files changed

+110
-8
lines changed

3 files changed

+110
-8
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ If you use Composer, these dependencies should be handled automatically.
3939

4040
### 2.x (v4 API, OAuth, PHP 8.0+)
4141

42-
Get your ConvertKit OAuth Client ID and Secret, and set it somewhere in your application.
42+
Please reach out to ConvertKit to set up an OAuth application for you. We'll provide you with your Client ID and Secret.
4343

4444
```php
4545
// Require the autoloader (if you're using a PHP framework, this may already be done for you).

src/ConvertKit_API.php

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,21 @@ class ConvertKit_API
2828
/**
2929
* ConvertKit OAuth Application Client ID
3030
*
31-
* @var boolean|string
31+
* @var string
3232
*/
33-
protected $client_id = false;
33+
protected $client_id = '';
3434

3535
/**
3636
* ConvertKit OAuth Application Client Secret
3737
*
38-
* @var boolean|string
38+
* @var string
3939
*/
40-
protected $client_secret = false;
40+
protected $client_secret = '';
4141

4242
/**
4343
* Access Token
4444
*
45-
* @var boolean|string
45+
* @var string
4646
*/
4747
protected $access_token = '';
4848

@@ -174,8 +174,34 @@ private function create_log(string $message)
174174
return;
175175
}
176176

177+
// Mask the Client ID, Client Secret and Access Token.
178+
$message = str_replace(
179+
$this->client_id,
180+
str_repeat('*', (strlen($this->client_id) - 4)) . substr($this->client_id, - 4),
181+
$message
182+
);
183+
$message = str_replace(
184+
$this->client_secret,
185+
str_repeat('*', (strlen($this->client_secret) - 4)) . substr($this->client_secret, - 4),
186+
$message
187+
);
188+
$message = str_replace(
189+
$this->access_token,
190+
str_repeat('*', (strlen($this->access_token) - 4)) . substr($this->access_token, - 4),
191+
$message
192+
);
193+
194+
// Mask email addresses that may be contained within the message.
195+
$message = preg_replace_callback(
196+
'^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})^',
197+
function ($matches) {
198+
return preg_replace('/\B[^@.]/', '*', $matches[0]);
199+
},
200+
$message
201+
);
202+
177203
// Add to log.
178-
$this->debug_logger->info($message);
204+
$this->debug_logger->info((string) $message);
179205
}
180206

181207
/**
@@ -1536,7 +1562,7 @@ private function strip_html_head_body_tags(string $markup)
15361562
* @return array<string, string|integer>
15371563
*/
15381564
private function build_pagination_params(
1539-
array $params = [],
1565+
array $params,
15401566
string $after_cursor = '',
15411567
string $before_cursor = '',
15421568
int $per_page = 100

tests/ConvertKitAPITest.php

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,65 @@ public function testDebugEnabledWithCustomLogFile()
151151
$this->assertStringContainsString('ck-debug.INFO: Finish request successfully', $this->getLogFileContents());
152152
}
153153

154+
/**
155+
* Test that debug logging works when enabled and an API call is made, with email addresses and credentials
156+
* masked in the log file.
157+
*
158+
* @since 2.0.0
159+
*
160+
* @return void
161+
*/
162+
public function testDebugCredentialsAndEmailsAreMasked()
163+
{
164+
// Setup API with debugging enabled.
165+
$api = new ConvertKit_API(
166+
clientID: $_ENV['CONVERTKIT_OAUTH_CLIENT_ID'],
167+
clientSecret: $_ENV['CONVERTKIT_OAUTH_CLIENT_SECRET'],
168+
accessToken: $_ENV['CONVERTKIT_OAUTH_ACCESS_TOKEN'],
169+
debug: true
170+
);
171+
172+
// Create log entries with Client ID, Client Secret, Access Token and Email Address, as if an API method
173+
// were to log this sensitive data.
174+
$this->callPrivateMethod($api, 'create_log', ['Client ID: ' . $_ENV['CONVERTKIT_OAUTH_CLIENT_ID']]);
175+
$this->callPrivateMethod($api, 'create_log', ['Client Secret: ' . $_ENV['CONVERTKIT_OAUTH_CLIENT_SECRET']]);
176+
$this->callPrivateMethod($api, 'create_log', ['Access Token: ' . $_ENV['CONVERTKIT_OAUTH_ACCESS_TOKEN']]);
177+
$this->callPrivateMethod($api, 'create_log', ['Email: ' . $_ENV['CONVERTKIT_API_SUBSCRIBER_EMAIL']]);
178+
179+
// Confirm that the log includes the masked Client ID, Secret, Access Token and Email Address.
180+
$this->assertStringContainsString(
181+
str_repeat(
182+
'*',
183+
(strlen($_ENV['CONVERTKIT_OAUTH_CLIENT_ID']) - 4)
184+
) . substr($_ENV['CONVERTKIT_OAUTH_CLIENT_ID'], -4),
185+
$this->getLogFileContents()
186+
);
187+
$this->assertStringContainsString(
188+
str_repeat(
189+
'*',
190+
(strlen($_ENV['CONVERTKIT_OAUTH_CLIENT_SECRET']) - 4)
191+
) . substr($_ENV['CONVERTKIT_OAUTH_CLIENT_SECRET'], -4),
192+
$this->getLogFileContents()
193+
);
194+
$this->assertStringContainsString(
195+
str_repeat(
196+
'*',
197+
(strlen($_ENV['CONVERTKIT_OAUTH_ACCESS_TOKEN']) - 4)
198+
) . substr($_ENV['CONVERTKIT_OAUTH_ACCESS_TOKEN'], -4),
199+
$this->getLogFileContents()
200+
);
201+
$this->assertStringContainsString(
202+
'o****@n********.c**',
203+
$this->getLogFileContents()
204+
);
205+
206+
// Confirm that the log does not include the unmasked Client ID, Secret, Access Token or Email Address.
207+
$this->assertStringNotContainsString($_ENV['CONVERTKIT_OAUTH_CLIENT_ID'], $this->getLogFileContents());
208+
$this->assertStringNotContainsString($_ENV['CONVERTKIT_OAUTH_CLIENT_SECRET'], $this->getLogFileContents());
209+
$this->assertStringNotContainsString($_ENV['CONVERTKIT_OAUTH_ACCESS_TOKEN'], $this->getLogFileContents());
210+
$this->assertStringNotContainsString($_ENV['CONVERTKIT_API_SUBSCRIBER_EMAIL'], $this->getLogFileContents());
211+
}
212+
154213
/**
155214
* Test that debug logging is not performed when disabled and an API call is made.
156215
*
@@ -2598,6 +2657,23 @@ private function getLogFileContents()
25982657
return file_get_contents($this->logFile);
25992658
}
26002659

2660+
/**
2661+
* Helper method to call a class' private method.
2662+
*
2663+
* @since 2.0.0
2664+
*
2665+
* @param mixed $obj Class Object.
2666+
* @param string $name Method Name.
2667+
* @param array $args Method Arguments.
2668+
*/
2669+
private function callPrivateMethod($obj, $name, array $args)
2670+
{
2671+
$class = new \ReflectionClass($obj);
2672+
$method = $class->getMethod($name);
2673+
$method->setAccessible(true);
2674+
return $method->invokeArgs($obj, $args);
2675+
}
2676+
26012677
/**
26022678
* Generates a unique email address for use in a test, comprising of a prefix,
26032679
* date + time and PHP version number.

0 commit comments

Comments
 (0)