1<?php 2 3/** 4 * This file is part of the FreeDSx SASL package. 5 * 6 * (c) Chad Sikorra <Chad.Sikorra@gmail.com> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace FreeDSx\Sasl; 13 14use FreeDSx\Sasl\Exception\SaslException; 15use FreeDSx\Sasl\Mechanism\AnonymousMechanism; 16use FreeDSx\Sasl\Mechanism\CramMD5Mechanism; 17use FreeDSx\Sasl\Mechanism\DigestMD5Mechanism; 18use FreeDSx\Sasl\Mechanism\MechanismInterface; 19use FreeDSx\Sasl\Mechanism\PlainMechanism; 20 21/** 22 * The main SASL class. 23 * 24 * @author Chad Sikorra <Chad.Sikorra@gmail.com> 25 */ 26class Sasl 27{ 28 /** 29 * @var MechanismInterface[] 30 */ 31 protected $mechanisms = []; 32 33 protected $options = [ 34 'supported' => [] 35 ]; 36 37 public function __construct(array $options = []) 38 { 39 $this->options = $options + $this->options; 40 $this->initMechs(); 41 } 42 43 /** 44 * Get a mechanism object by its name. 45 */ 46 public function get(string $mechanism): MechanismInterface 47 { 48 $mech = $this->mechanisms[$mechanism] ?? null; 49 50 if ($mech === null) { 51 throw new SaslException('The mechanism "%s" is not supported.'); 52 } 53 54 return $mech; 55 } 56 57 /** 58 * Whether or not the mechanism is supported. 59 */ 60 public function supports(string $mechanism): bool 61 { 62 return isset($this->mechanisms()[$mechanism]); 63 } 64 65 /** 66 * Add a mechanism object. 67 * 68 * @return Sasl 69 */ 70 public function add(MechanismInterface $mechanism): self 71 { 72 $this->mechanisms[$mechanism->getName()] = $mechanism; 73 74 return $this; 75 } 76 77 /** 78 * Remove a mechanism by its name. 79 * 80 * @return Sasl 81 */ 82 public function remove(string $mechanism): self 83 { 84 if (isset($this->mechanisms[$mechanism])) { 85 unset($this->mechanisms[$mechanism]); 86 } 87 88 return $this; 89 } 90 91 /** 92 * Given an array of mechanism names, and optional options, select the best supported mechanism available. 93 * 94 * @param string[] $choices array of mechanisms by their name 95 * @param array $options array of options (ie. ['use_integrity' => true]) 96 * @return MechanismInterface the mechanism selected. 97 * @throws SaslException if no supported mechanism could be found. 98 */ 99 public function select(array $choices = [], array $options = []): MechanismInterface 100 { 101 $selector = new MechanismSelector($this->mechanisms()); 102 103 return $selector->select($choices, $options); 104 } 105 106 /** 107 * @return MechanismInterface[] 108 */ 109 public function mechanisms(): array 110 { 111 return $this->mechanisms; 112 } 113 114 protected function initMechs(): void 115 { 116 $this->mechanisms = [ 117 DigestMD5Mechanism::NAME => new DigestMD5Mechanism(), 118 CramMD5Mechanism::NAME => new CramMD5Mechanism(), 119 PlainMechanism::NAME => new PlainMechanism(), 120 AnonymousMechanism::NAME => new AnonymousMechanism(), 121 ]; 122 123 if (is_array($this->options['supported']) && !empty($this->options['supported'])) { 124 foreach (array_keys($this->mechanisms) as $mechName) { 125 if (!in_array($mechName, $this->options['supported'], true)) { 126 unset($this->mechanisms[$mechName]); 127 } 128 } 129 } 130 } 131} 132