Enable Discovery debug logging for production troubleshooting

- Add DISCOVERY_LOG_LEVEL=debug
- Add DISCOVERY_SHOW_PROGRESS=true
- Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
2025-08-11 20:13:26 +02:00
parent 59fd3dd3b1
commit 55a330b223
3683 changed files with 2956207 additions and 16948 deletions

View File

@@ -0,0 +1,360 @@
<?php
declare(strict_types=1);
use App\Framework\Cryptography\SecureToken;
use App\Framework\Cryptography\SecureTokenGenerator;
it('creates secure token with valid parameters', function () {
$token = new SecureToken(
value: 'test-token-value',
type: SecureTokenGenerator::TYPE_API_KEY,
format: SecureTokenGenerator::FORMAT_BASE64_URL,
length: 32,
prefix: 'ak',
rawBytes: 'raw-bytes-data',
metadata: ['purpose' => 'testing']
);
expect($token->getValue())->toBe('test-token-value');
expect($token->getType())->toBe(SecureTokenGenerator::TYPE_API_KEY);
expect($token->getFormat())->toBe(SecureTokenGenerator::FORMAT_BASE64_URL);
expect($token->getLength())->toBe(32);
expect($token->getPrefix())->toBe('ak');
expect($token->getRawBytes())->toBe('raw-bytes-data');
expect($token->getMetadata())->toBe(['purpose' => 'testing']);
});
it('throws exception for empty token value', function () {
expect(fn () => new SecureToken(
value: '',
type: SecureTokenGenerator::TYPE_API_KEY,
format: SecureTokenGenerator::FORMAT_BASE64_URL,
length: 32,
prefix: null,
rawBytes: 'raw-bytes',
metadata: []
))->toThrow(InvalidArgumentException::class, 'Token value cannot be empty');
});
it('provides string representation', function () {
$tokenValue = 'test-token-value';
$token = new SecureToken(
value: $tokenValue,
type: SecureTokenGenerator::TYPE_SESSION,
format: SecureTokenGenerator::FORMAT_BASE64_URL,
length: 32,
prefix: null,
rawBytes: 'raw-bytes',
metadata: []
);
expect($token->toString())->toBe($tokenValue);
expect((string)$token)->toBe($tokenValue);
});
it('extracts value without prefix', function () {
$token = new SecureToken(
value: 'ak_abcd1234efgh5678',
type: SecureTokenGenerator::TYPE_API_KEY,
format: SecureTokenGenerator::FORMAT_BASE64_URL,
length: 32,
prefix: 'ak',
rawBytes: 'raw-bytes',
metadata: []
);
expect($token->getValueWithoutPrefix())->toBe('abcd1234efgh5678');
expect($token->hasPrefix())->toBeTrue();
});
it('handles token without prefix', function () {
$token = new SecureToken(
value: 'abcd1234efgh5678',
type: SecureTokenGenerator::TYPE_SESSION,
format: SecureTokenGenerator::FORMAT_BASE64_URL,
length: 32,
prefix: null,
rawBytes: 'raw-bytes',
metadata: []
);
expect($token->getValueWithoutPrefix())->toBe('abcd1234efgh5678');
expect($token->hasPrefix())->toBeFalse();
});
it('provides raw bytes in different formats', function () {
$rawBytes = "\x01\x02\x03\x04";
$token = new SecureToken(
value: 'test-token',
type: SecureTokenGenerator::TYPE_API_KEY,
format: SecureTokenGenerator::FORMAT_BASE64_URL,
length: 32,
prefix: null,
rawBytes: $rawBytes,
metadata: []
);
expect($token->getRawBytesHex())->toBe('01020304');
expect($token->getRawBytesBase64())->toBe(base64_encode($rawBytes));
});
it('checks equality correctly with timing-safe comparison', function () {
$tokenValue = 'same-token-value';
$token1 = new SecureToken(
value: $tokenValue,
type: SecureTokenGenerator::TYPE_API_KEY,
format: SecureTokenGenerator::FORMAT_BASE64_URL,
length: 32,
prefix: null,
rawBytes: 'raw-bytes',
metadata: []
);
$token2 = new SecureToken(
value: $tokenValue,
type: SecureTokenGenerator::TYPE_SESSION,
format: SecureTokenGenerator::FORMAT_HEX,
length: 64,
prefix: 'test',
rawBytes: 'different-raw',
metadata: ['different' => 'metadata']
);
$token3 = new SecureToken(
value: 'different-token-value',
type: SecureTokenGenerator::TYPE_API_KEY,
format: SecureTokenGenerator::FORMAT_BASE64_URL,
length: 32,
prefix: null,
rawBytes: 'raw-bytes',
metadata: []
);
expect($token1->equals($token2))->toBeTrue(); // Same value
expect($token1->equals($token3))->toBeFalse(); // Different value
});
it('verifies token value with timing-safe comparison', function () {
$tokenValue = 'secure-token-value';
$token = new SecureToken(
value: $tokenValue,
type: SecureTokenGenerator::TYPE_API_KEY,
format: SecureTokenGenerator::FORMAT_BASE64_URL,
length: 32,
prefix: null,
rawBytes: 'raw-bytes',
metadata: []
);
expect($token->verify($tokenValue))->toBeTrue();
expect($token->verify('wrong-token-value'))->toBeFalse();
});
it('identifies token types correctly', function () {
$apiKeyToken = new SecureToken(
value: 'test-token',
type: SecureTokenGenerator::TYPE_API_KEY,
format: SecureTokenGenerator::FORMAT_BASE64_URL,
length: 32,
prefix: null,
rawBytes: 'raw-bytes',
metadata: []
);
$sessionToken = new SecureToken(
value: 'test-token',
type: SecureTokenGenerator::TYPE_SESSION,
format: SecureTokenGenerator::FORMAT_BASE64_URL,
length: 32,
prefix: null,
rawBytes: 'raw-bytes',
metadata: []
);
expect($apiKeyToken->isApiKey())->toBeTrue();
expect($apiKeyToken->isSessionToken())->toBeFalse();
expect($sessionToken->isSessionToken())->toBeTrue();
expect($sessionToken->isApiKey())->toBeFalse();
});
it('checks token properties from metadata', function () {
$singleUseToken = new SecureToken(
value: 'test-token',
type: SecureTokenGenerator::TYPE_VERIFICATION,
format: SecureTokenGenerator::FORMAT_BASE64_URL,
length: 32,
prefix: null,
rawBytes: 'raw-bytes',
metadata: ['single_use' => true, 'long_lived' => false]
);
$longLivedToken = new SecureToken(
value: 'test-token',
type: SecureTokenGenerator::TYPE_REFRESH,
format: SecureTokenGenerator::FORMAT_BASE64_URL,
length: 64,
prefix: null,
rawBytes: 'raw-bytes',
metadata: ['single_use' => false, 'long_lived' => true]
);
expect($singleUseToken->isSingleUse())->toBeTrue();
expect($singleUseToken->isLongLived())->toBeFalse();
expect($longLivedToken->isLongLived())->toBeTrue();
expect($longLivedToken->isSingleUse())->toBeFalse();
});
it('gets specific metadata values', function () {
$token = new SecureToken(
value: 'test-token',
type: SecureTokenGenerator::TYPE_API_KEY,
format: SecureTokenGenerator::FORMAT_BASE64_URL,
length: 32,
prefix: null,
rawBytes: 'raw-bytes',
metadata: ['purpose' => 'api_auth', 'expires' => 3600]
);
expect($token->getMetadataValue('purpose'))->toBe('api_auth');
expect($token->getMetadataValue('expires'))->toBe(3600);
expect($token->getMetadataValue('nonexistent', 'default'))->toBe('default');
});
it('calculates token age', function () {
$token = new SecureToken(
value: 'test-token',
type: SecureTokenGenerator::TYPE_SESSION,
format: SecureTokenGenerator::FORMAT_BASE64_URL,
length: 32,
prefix: null,
rawBytes: 'raw-bytes',
metadata: []
);
sleep(1);
$age = $token->getAgeInSeconds();
expect($age)->toBeGreaterThanOrEqual(1);
});
it('exports to and imports from array', function () {
$originalToken = new SecureToken(
value: 'test-token-value',
type: SecureTokenGenerator::TYPE_API_KEY,
format: SecureTokenGenerator::FORMAT_BASE64_URL,
length: 32,
prefix: 'ak',
rawBytes: 'raw-bytes-data',
metadata: ['purpose' => 'testing', 'scope' => 'read']
);
$array = $originalToken->toArray();
$restoredToken = SecureToken::fromArray($array);
expect($restoredToken->getValue())->toBe($originalToken->getValue());
expect($restoredToken->getType())->toBe($originalToken->getType());
expect($restoredToken->getFormat())->toBe($originalToken->getFormat());
expect($restoredToken->getLength())->toBe($originalToken->getLength());
expect($restoredToken->getPrefix())->toBe($originalToken->getPrefix());
expect($restoredToken->getRawBytes())->toBe($originalToken->getRawBytes());
expect($restoredToken->getMetadata())->toBe($originalToken->getMetadata());
});
it('provides safe summary without sensitive data', function () {
$token = new SecureToken(
value: 'very-secret-token-value',
type: SecureTokenGenerator::TYPE_API_KEY,
format: SecureTokenGenerator::FORMAT_BASE64_URL,
length: 32,
prefix: 'ak',
rawBytes: 'raw-bytes',
metadata: ['purpose' => 'api_auth', 'scope' => 'admin']
);
$summary = $token->getSafeSummary();
expect($summary)->toHaveKey('type');
expect($summary)->toHaveKey('format');
expect($summary)->toHaveKey('length');
expect($summary)->toHaveKey('prefix');
expect($summary)->toHaveKey('has_prefix');
expect($summary)->toHaveKey('metadata_keys');
expect($summary)->not->toHaveKey('value'); // Should not contain sensitive value
expect($summary)->not->toHaveKey('raw_bytes'); // Should not contain raw bytes
expect($summary['metadata_keys'])->toBe(['purpose', 'scope']);
});
it('generates fingerprint for identification', function () {
$token = new SecureToken(
value: 'test-token-value',
type: SecureTokenGenerator::TYPE_API_KEY,
format: SecureTokenGenerator::FORMAT_BASE64_URL,
length: 32,
prefix: null,
rawBytes: 'raw-bytes',
metadata: []
);
$fingerprint = $token->getFingerprint();
$shortFingerprint = $token->getShortFingerprint();
expect($fingerprint)->toHaveLength(64); // SHA-256 hex
expect($shortFingerprint)->toHaveLength(16);
expect($fingerprint)->toStartWith($shortFingerprint);
});
it('masks token value for safe logging', function () {
$longToken = new SecureToken(
value: 'this-is-a-very-long-token-value-for-testing',
type: SecureTokenGenerator::TYPE_API_KEY,
format: SecureTokenGenerator::FORMAT_BASE64_URL,
length: 32,
prefix: null,
rawBytes: 'raw-bytes',
metadata: []
);
$shortToken = new SecureToken(
value: 'short',
type: SecureTokenGenerator::TYPE_API_KEY,
format: SecureTokenGenerator::FORMAT_BASE64_URL,
length: 32,
prefix: null,
rawBytes: 'raw-bytes',
metadata: []
);
$longMasked = $longToken->getMaskedValue();
$shortMasked = $shortToken->getMaskedValue();
expect($longMasked)->toStartWith('this');
expect($longMasked)->toEndWith('ting');
expect($longMasked)->toContain('*');
expect($shortMasked)->toBe('***'); // Short tokens fully masked
});
it('throws exception for missing required fields in fromArray', function () {
$incompleteData = [
'value' => 'test-token',
'type' => SecureTokenGenerator::TYPE_API_KEY,
// Missing format, length, raw_bytes
];
expect(fn () => SecureToken::fromArray($incompleteData))
->toThrow(InvalidArgumentException::class, 'Missing required field');
});
it('throws exception for invalid base64 raw bytes in fromArray', function () {
$invalidData = [
'value' => 'test-token',
'type' => SecureTokenGenerator::TYPE_API_KEY,
'format' => SecureTokenGenerator::FORMAT_BASE64_URL,
'length' => 32,
'raw_bytes' => 'invalid-base64!@#',
];
expect(fn () => SecureToken::fromArray($invalidData))
->toThrow(InvalidArgumentException::class, 'Invalid Base64 raw bytes');
});