Plugin Directory


Ignore:
Timestamp:
11/14/2026 10:49:34 PM (less than one hour ago)
Author:
erfanteymouri
Message:

Aiwend 0.10.0 (Aiwend – AI Chatbot): English-only public beta, updated listing and seven screenshots, revised banners and Stable Tag 0.10.0

File:
1 edited

Legend:

Unmodified
Added
Removed
  • aiwend/trunk/src/Secrets/ConnectionRepository.php

    r3787448 r3802562  
    1515final class ConnectionRepository
    1616{
    17     public function __construct(
    18         private Connection $db,
    19         private Vault $vault,
    20         private Redactor $redactor,
    21         private Clock $clock,
    22     ) {
    23     }
    24 
    25     public function create(int $siteId, string $provider, string $label, string $secret): int
    26     {
     17    private Connection $db;
     18    private Vault $vault;
     19    private Redactor $redactor;
     20    private Clock $clock;
     21    public function __construct(Connection $db, Vault $vault, Redactor $redactor, Clock $clock)
     22    {
     23        $this->db = $db;
     24        $this->vault = $vault;
     25        $this->redactor = $redactor;
     26        $this->clock = $clock;
     27    }
     28    /**
     29     * @param mixed $secret a SecretString (production callers) or a string (tests, legacy callers); it is carried as a SecretString from here on
     30     */
     31    public function create(
     32        int $siteId,
     33        string $provider,
     34        string $label,
     35        #[\SensitiveParameter]
     36        $secret
     37    ): int {
    2738        $this->assertProvider($provider);
    28         $secret = trim($secret);
    29         if ($secret === '' || strlen($secret) > 4096) {
     39        $secret = self::trimmed($secret);
     40        $length = $secret->length();
     41        if ($length < 1 || $length > 4096) {
    3042            throw new \InvalidArgumentException('Credential must be between 1 and 4096 characters.');
    3143        }
    … …  
    6173    }
    6274
    63     public function replaceSecret(int $siteId, int $id, string $secret): void
    64     {
     75    /**
     76     * @param mixed $secret a SecretString (production callers) or a string (tests, legacy callers)
     77     */
     78    public function replaceSecret(
     79        int $siteId,
     80        int $id,
     81        #[\SensitiveParameter]
     82        $secret
     83    ): void {
    6584        $row = $this->requireRow($siteId, $id);
    66         if (\Aiwend\Provider\Providers::looksLikeAnotherProvider((string) $row['provider'], trim($secret))) {
     85        $secret = self::trimmed($secret);
     86        if (\Aiwend\Provider\Providers::looksLikeAnotherProvider((string) $row['provider'], $secret)) {
    6787            throw new \InvalidArgumentException('key_wrong_provider');
    6888        }
    69         $this->storeSecret($siteId, $id, trim($secret));
     89        $this->storeSecret($siteId, $id, $secret);
    7090        $this->db->execute('UPDATE {connections} SET status = ? WHERE id = ? AND site_id = ?', ['unverified', $id, $siteId]);
    7191    }
    … …  
    126146
    127147    /** Server-side only. The returned value is registered with the redactor. */
    128     public function secretForDispatch(int $siteId, int $id): string
     148    public function secretForDispatch(int $siteId, int $id): SecretString
    129149    {
    130150        $row = $this->requireRow($siteId, $id);
    … …  
    132152            throw new \RuntimeException('Connection is disabled.');
    133153        }
    134         $secret = $this->vault->decrypt((string) $row['ciphertext'], $this->context($siteId, $id));
     154        $secret = new SecretString($this->vault->decrypt((string) $row['ciphertext'], $this->context($siteId, $id)));
    135155        $this->redactor->remember($secret);
    136156        return $secret;
    … …  
    161181                }
    162182                $context = $this->context($siteId, $id);
    163                 $plain = $this->vault->decrypt($old, $context);
     183                $plain = new SecretString($this->vault->decrypt($old, $context));
    164184                $new = $this->vault->encrypt($plain, $context);
    165                 if ($this->vault->needsRotation($new) || !hash_equals($plain, $this->vault->decrypt($new, $context))) {
     185                if ($this->vault->needsRotation($new) || !hash_equals($plain->reveal(), $this->vault->decrypt($new, $context))) {
    166186                    throw new \RuntimeException('Re-encrypted credential did not verify.');
    167187                }
    … …  
    171191                );
    172192                $changed === 1 ? $rotated++ : $failed++;
    173             } catch (\RuntimeException | \SodiumException) {
     193            } catch (\RuntimeException | \SodiumException $exception) {
    174194                $failed++;
    175195            }
    … …  
    188208            try {
    189209                $pending += $this->vault->needsRotation((string) $row['ciphertext']) ? 1 : 0;
    190             } catch (\RuntimeException) {
     210            } catch (\RuntimeException $exception) {
    191211                $pending++;
    192212            }
    … …  
    202222            try {
    203223                $pending += $this->vault->needsRotation((string) $row['ciphertext']) ? 1 : 0;
    204             } catch (\RuntimeException) {
     224            } catch (\RuntimeException $exception) {
    205225                $pending++;
    206226            }
    … …  
    209229    }
    210230
    211     private function storeSecret(int $siteId, int $id, string $secret): void
    212     {
    213         if ($secret === '' || strlen($secret) > 4096) {
     231    /** Surrounding whitespace is not part of a credential. */
     232    private static function trimmed(
     233        #[\SensitiveParameter]
     234        $secret
     235    ): SecretString {
     236        $secret = SecretString::from($secret);
     237        $plain = $secret->reveal();
     238        return $plain === trim($plain) ? $secret : new SecretString(trim($plain));
     239    }
     240
     241    private function storeSecret(int $siteId, int $id, SecretString $secret): void
     242    {
     243        $length = $secret->length();
     244        if ($length < 1 || $length > 4096) {
    214245            throw new \InvalidArgumentException('Credential must be between 1 and 4096 characters.');
    215246        }
Note: See TracChangeset for help on using the changeset viewer.