1<?php 2 3/** 4 * This file is part of the FreeDSx LDAP 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\Ldap\Operation\Request; 13 14use FreeDSx\Asn1\Asn1; 15use FreeDSx\Asn1\Type\AbstractType; 16use FreeDSx\Asn1\Type\BooleanType; 17use FreeDSx\Asn1\Type\IncompleteType; 18use FreeDSx\Asn1\Type\OctetStringType; 19use FreeDSx\Asn1\Type\SequenceType; 20use FreeDSx\Ldap\Entry\Dn; 21use FreeDSx\Ldap\Entry\Rdn; 22use FreeDSx\Ldap\Exception\ProtocolException; 23use FreeDSx\Ldap\Protocol\LdapEncoder; 24use function count; 25 26/** 27 * A Modify DN Request. RFC 4511, 4.9 28 * 29 * ModifyDNRequest ::= [APPLICATION 12] SEQUENCE { 30 * entry LDAPDN, 31 * newrdn RelativeLDAPDN, 32 * deleteoldrdn BOOLEAN, 33 * newSuperior [0] LDAPDN OPTIONAL } 34 * 35 * @author Chad Sikorra <Chad.Sikorra@gmail.com> 36 */ 37class ModifyDnRequest implements RequestInterface, DnRequestInterface 38{ 39 protected const APP_TAG = 12; 40 41 /** 42 * @var Dn 43 */ 44 protected $dn; 45 46 /** 47 * @var Rdn 48 */ 49 protected $newRdn; 50 51 /** 52 * @var bool 53 */ 54 protected $deleteOldRdn; 55 56 /** 57 * @var null|Dn 58 */ 59 protected $newParentDn; 60 61 /** 62 * @param string|Dn $dn 63 * @param string|Rdn $newRdn 64 * @param bool $deleteOldRdn 65 * @param null|string|Dn $newParentDn 66 */ 67 public function __construct($dn, $newRdn, bool $deleteOldRdn, $newParentDn = null) 68 { 69 $this->setDn($dn); 70 $this->setNewRdn($newRdn); 71 $this->setNewParentDn($newParentDn); 72 $this->deleteOldRdn = $deleteOldRdn; 73 } 74 75 /** 76 * @return Dn 77 */ 78 public function getDn(): Dn 79 { 80 return $this->dn; 81 } 82 83 /** 84 * @param string|Dn $dn 85 * @return $this 86 */ 87 public function setDn($dn) 88 { 89 $this->dn = $dn instanceof Dn ? $dn : new Dn($dn); 90 91 return $this; 92 } 93 94 /** 95 * @return Rdn 96 */ 97 public function getNewRdn(): Rdn 98 { 99 return $this->newRdn; 100 } 101 102 /** 103 * @param string|Rdn $newRdn 104 * @return $this 105 */ 106 public function setNewRdn($newRdn) 107 { 108 $this->newRdn = $newRdn instanceof Rdn ? $newRdn : Rdn::create($newRdn); 109 110 return $this; 111 } 112 113 /** 114 * @return bool 115 */ 116 public function getDeleteOldRdn(): bool 117 { 118 return $this->deleteOldRdn; 119 } 120 121 /** 122 * @param bool $deleteOldRdn 123 * @return $this 124 */ 125 public function setDeleteOldRdn(bool $deleteOldRdn) 126 { 127 $this->deleteOldRdn = $deleteOldRdn; 128 129 return $this; 130 } 131 132 /** 133 * @return null|Dn 134 */ 135 public function getNewParentDn(): ?Dn 136 { 137 return $this->newParentDn; 138 } 139 140 /** 141 * @param null|string|Dn $newParentDn 142 * @return $this 143 */ 144 public function setNewParentDn($newParentDn) 145 { 146 if ($newParentDn !== null) { 147 $newParentDn = $newParentDn instanceof Dn ? $newParentDn : new Dn($newParentDn); 148 } 149 $this->newParentDn = $newParentDn; 150 151 return $this; 152 } 153 154 /** 155 * {@inheritDoc} 156 * @return self 157 */ 158 public static function fromAsn1(AbstractType $type) 159 { 160 if (!($type instanceof SequenceType && count($type) >= 3 && count($type) <= 4)) { 161 throw new ProtocolException('The modify dn request is malformed'); 162 } 163 $entry = $type->getChild(0); 164 $newRdn = $type->getChild(1); 165 $deleteOldRdn = $type->getChild(2); 166 $newSuperior = $type->getChild(3); 167 168 if (!($entry instanceof OctetStringType && $newRdn instanceof OctetStringType && $deleteOldRdn instanceof BooleanType)) { 169 throw new ProtocolException('The modify dn request is malformed'); 170 } 171 if ($newSuperior !== null && !($newSuperior->getTagClass() === AbstractType::TAG_CLASS_CONTEXT_SPECIFIC && $newSuperior->getTagNumber() === 0)) { 172 throw new ProtocolException('The modify dn request is malformed'); 173 } 174 $newSuperior = ($newSuperior instanceof IncompleteType) 175 ? (new LdapEncoder())->complete($newSuperior, AbstractType::TAG_TYPE_OCTET_STRING) 176 : $newSuperior; 177 $newSuperior = ($newSuperior !== null) ? $newSuperior->getValue() : null; 178 179 return new self($entry->getValue(), $newRdn->getValue(), $deleteOldRdn->getValue(), $newSuperior); 180 } 181 182 /** 183 * @return SequenceType 184 */ 185 public function toAsn1(): AbstractType 186 { 187 /** @var \FreeDSx\Asn1\Type\SequenceType $asn1 */ 188 $asn1 = Asn1::application(self::APP_TAG, Asn1::sequence( 189 Asn1::octetString($this->dn->toString()), 190 // @todo Make a RDN type. Future validation purposes? 191 Asn1::octetString($this->newRdn->toString()), 192 Asn1::boolean($this->deleteOldRdn) 193 )); 194 if ($this->newParentDn !== null) { 195 $asn1->addChild(Asn1::context(0, Asn1::octetString($this->newParentDn->toString()))); 196 } 197 198 return $asn1; 199 } 200} 201