1<?php 2/** 3 * This file is part of the FreeDSx LDAP package. 4 * 5 * (c) Chad Sikorra <Chad.Sikorra@gmail.com> 6 * 7 * For the full copyright and license information, please view the LICENSE 8 * file that was distributed with this source code. 9 */ 10 11namespace FreeDSx\Ldap\Control; 12 13use FreeDSx\Asn1\Asn1; 14use FreeDSx\Asn1\Type\AbstractType; 15use FreeDSx\Asn1\Type\IncompleteType; 16use FreeDSx\Asn1\Type\SequenceType; 17use FreeDSx\Ldap\Exception\ProtocolException; 18use FreeDSx\Ldap\Protocol\LdapEncoder; 19 20/** 21 * Represents a password policy response. draft-behera-ldap-password-policy-09 22 * 23 * PasswordPolicyResponseValue ::= SEQUENCE { 24 * warning [0] CHOICE { 25 * timeBeforeExpiration [0] INTEGER (0 .. maxInt), 26 * graceAuthNsRemaining [1] INTEGER (0 .. maxInt) } OPTIONAL, 27 * error [1] ENUMERATED { 28 * passwordExpired (0), 29 * accountLocked (1), 30 * changeAfterReset (2), 31 * passwordModNotAllowed (3), 32 * mustSupplyOldPassword (4), 33 * insufficientPasswordQuality (5), 34 * passwordTooShort (6), 35 * passwordTooYoung (7), 36 * passwordInHistory (8) } OPTIONAL } 37 * 38 * @author Chad Sikorra <Chad.Sikorra@gmail.com> 39 */ 40class PwdPolicyResponseControl extends Control 41{ 42 /** 43 * @var int|null 44 */ 45 protected $timeBeforeExpiration; 46 47 /** 48 * @var int|null 49 */ 50 protected $graceAuthRemaining; 51 52 /** 53 * @var int|null 54 */ 55 protected $error; 56 57 /** 58 * @param int|null $timeBeforeExpiration 59 * @param int|null $graceAuthRemaining 60 * @param int|null $error 61 */ 62 public function __construct(?int $timeBeforeExpiration = null, ?int $graceAuthRemaining = null, ?int $error = null) 63 { 64 $this->timeBeforeExpiration = $timeBeforeExpiration; 65 $this->graceAuthRemaining = $graceAuthRemaining; 66 $this->error = $error; 67 parent::__construct(self::OID_PWD_POLICY); 68 } 69 70 /** 71 * @return int|null 72 */ 73 public function getTimeBeforeExpiration(): ?int 74 { 75 return $this->timeBeforeExpiration; 76 } 77 78 /** 79 * @return int|null 80 */ 81 public function getGraceAttemptsRemaining(): ?int 82 { 83 return $this->graceAuthRemaining; 84 } 85 86 /** 87 * @return int|null 88 */ 89 public function getError(): ?int 90 { 91 return $this->error; 92 } 93 94 /** 95 * {@inheritdoc} 96 */ 97 public function toAsn1(): AbstractType 98 { 99 $response = Asn1::sequence(); 100 $warning = null; 101 102 if ($this->graceAuthRemaining !== null && $this->timeBeforeExpiration !== null) { 103 throw new ProtocolException('The password policy response cannot have both a time expiration and a grace auth value.'); 104 } 105 if ($this->timeBeforeExpiration !== null) { 106 $warning = Asn1::context(0, Asn1::sequence( 107 Asn1::context(0, Asn1::integer($this->timeBeforeExpiration)) 108 )); 109 } 110 if ($this->graceAuthRemaining !== null) { 111 $warning = Asn1::context(0, Asn1::sequence( 112 Asn1::context(1, Asn1::integer($this->graceAuthRemaining)) 113 )); 114 } 115 116 if ($warning !== null) { 117 $response->addChild($warning); 118 } 119 if ($this->error !== null) { 120 $response->addChild(Asn1::context(1, Asn1::enumerated($this->error))); 121 } 122 $this->controlValue = $response; 123 124 return parent::toAsn1(); 125 } 126 127 /** 128 * {@inheritdoc} 129 */ 130 public static function fromAsn1(AbstractType $type) 131 { 132 /** @var SequenceType $response */ 133 $response = self::decodeEncodedValue($type); 134 135 $error = null; 136 $timeBeforeExpiration = null; 137 $graceAttemptsRemaining = null; 138 139 $encoder = new LdapEncoder(); 140 foreach ($response->getChildren() as $child) { 141 if (!$child instanceof IncompleteType) { 142 throw new ProtocolException('The ASN1 structure for the pwdPolicy control is malformed.'); 143 } 144 if ($child->getTagNumber() === 0) { 145 $warnings = $encoder->complete($child, AbstractType::TAG_TYPE_SEQUENCE, [ 146 AbstractType::TAG_CLASS_CONTEXT_SPECIFIC => [ 147 0 => AbstractType::TAG_TYPE_INTEGER, 148 1 => AbstractType::TAG_TYPE_INTEGER, 149 ], 150 ]); 151 /** @var AbstractType $warning */ 152 foreach ($warnings->getChildren() as $warning) { 153 if ($warning->getTagNumber() === 0) { 154 $timeBeforeExpiration = $warning->getValue(); 155 break; 156 } elseif ($warning->getTagNumber() === 1) { 157 $graceAttemptsRemaining = $warning->getValue(); 158 break; 159 } 160 } 161 } elseif ($child->getTagNumber() === 1) { 162 $error = $encoder->complete($child, AbstractType::TAG_TYPE_ENUMERATED)->getValue(); 163 } 164 } 165 $control = new self($timeBeforeExpiration, $graceAttemptsRemaining, $error); 166 167 return self::mergeControlData($control, $type); 168 } 169} 170