getEncryptionKey(); $this->defineTable(); } protected function defineTable():void { $table = CustomTable::for('integrations'); $table->setColumns([ 'id' => 'bigint(20) unsigned NOT NULL AUTO_INCREMENT', 'user_id' => $table->getUserIDType().' NOT NULL', 'integration' => "ENUM('bluesky','cloudflare','facebook','google-maps','gmb','helcim','instagram','postmark','square','umami') NOT NULL", 'credentials' => 'varchar(255) DEFAULT NULL', 'created_at' => 'datetime DEFAULT CURRENT_TIMESTAMP', 'updated_at' => 'datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP' ]); $table->setKeys([ ['key' => 'PRIMARY', 'value' => '(`id`)'], ['key' => 'UNIQUE', 'value' => '`user_integration` (`user_id`, `integration`)'], '`user` (`user_id`)', ]); $base = BASE; $table->setConstraints([ "CONSTRAINT `{$base}integrations_user` FOREIGN KEY (`user_id`) REFERENCES `{$table->getUserTable()}` (`ID`) ON DELETE CASCADE" ]); $table->defineTable(); $this->table = $table; } public static function getInstance(): CredentialsManager { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } /** * Store encrypted credentials */ public function storeCredentials(string $service, array $credentials, ?int $userID = null): bool { $find = $this->findBy($service, $userID); $update = [ 'credentials' => $this->encrypt(json_encode($credentials)) ]; return (bool)$this->table->findOrCreate($find, $update); } /** * Retrieve and decrypt credentials */ public function getCredentials(string $service, ?int $userID = null): array { $find = $this->findBy($service, $userID); $credentials = $this->table->pluck('credentials', $find); if (empty($credentials)) { return []; } $decrypted_data = $this->decrypt($credentials[0]); return empty($decrypted_data) ? [] : json_decode($decrypted_data, true); } /** * Delete credentials */ public function deleteCredentials(string $service, ?int $userID = null): bool { $find = $this->findBy($service, $userID); return $this->table->delete($find); } protected function findBy(string $service, ?int $userID = null):array { return [ 'user_id' => is_null($userID) ? 0 : $userID, 'integration'=> $service ]; } /** * Check if credentials exist */ public function hasCredentials(string $service, ?int $userID = null): bool { return !empty($this->getCredentials($service, $userID)); } /** * Get or create encryption key */ private function getEncryptionKey():void { $this->encryption_key = JVB_KEY; } /** * Encrypt data */ private function encrypt(string $data): string { $iv = random_bytes(16); $encrypted = openssl_encrypt($data, 'AES-256-CBC', $this->encryption_key, 0, $iv); return base64_encode($iv . $encrypted); } /** * Decrypt data */ private function decrypt(string $data): ?string { $data = base64_decode($data); $iv = substr($data, 0, 16); $encrypted = substr($data, 16); return openssl_decrypt($encrypted, 'AES-256-CBC', $this->encryption_key, 0, $iv); } /** * Get all users with credentials for a service */ public function getUsersWithCredentials(string $service): array { return $this->table->pluck('user_id', ['integration' => $service]); } }