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\Operation\Request; 12 13use FreeDSx\Asn1\Asn1; 14use FreeDSx\Asn1\Type\AbstractType; 15use FreeDSx\Asn1\Type\OctetStringType; 16use FreeDSx\Asn1\Type\SequenceType; 17use FreeDSx\Asn1\Type\SetType; 18use FreeDSx\Ldap\Entry\Attribute; 19use FreeDSx\Ldap\Entry\Dn; 20use FreeDSx\Ldap\Entry\Entry; 21use FreeDSx\Ldap\Exception\ProtocolException; 22 23/** 24 * A request to add an entry to LDAP. RFC 4511, 4.7. 25 * 26 * AddRequest ::= [APPLICATION 8] SEQUENCE { 27 * entry LDAPDN, 28 * attributes AttributeList } 29 * 30 * AttributeList ::= SEQUENCE OF attribute Attribute 31 * 32 * PartialAttribute ::= SEQUENCE { 33 * type AttributeDescription, 34 * vals SET OF value AttributeValue } 35 * 36 * Attribute ::= PartialAttribute(WITH COMPONENTS { 37 * ..., 38 * vals (SIZE(1..MAX))}) 39 * 40 * AttributeDescription ::= LDAPString 41 * 42 * AttributeValue ::= OCTET STRING 43 * 44 * @author Chad Sikorra <Chad.Sikorra@gmail.com> 45 */ 46class AddRequest implements RequestInterface 47{ 48 protected const APP_TAG = 8; 49 50 /** 51 * @var Entry 52 */ 53 protected $entry; 54 55 /** 56 * @param Entry $entry 57 */ 58 public function __construct(Entry $entry) 59 { 60 $this->entry = $entry; 61 } 62 63 /** 64 * @return Entry 65 */ 66 public function getEntry(): Entry 67 { 68 return $this->entry; 69 } 70 71 /** 72 * @param Entry $entry 73 * @return $this 74 */ 75 public function setEntry(Entry $entry) 76 { 77 $this->entry = $entry; 78 79 return $this; 80 } 81 82 /** 83 * {@inheritdoc} 84 */ 85 public static function fromAsn1(AbstractType $type) 86 { 87 if (!($type instanceof SequenceType && count($type) === 2)) { 88 throw new ProtocolException('The add request is malformed: %s'); 89 } 90 91 $dn = $type->getChild(0); 92 $attrList = $type->getChild(1); 93 if (!($dn instanceof OctetStringType && $attrList instanceof SequenceType)) { 94 throw new ProtocolException('The add request is malformed.'); 95 } 96 $dn = new Dn($dn->getValue()); 97 98 $attributes = []; 99 foreach ($attrList->getChildren() as $attrListing) { 100 if (!($attrListing instanceof SequenceType && \count($attrListing->getChildren()) == 2)) { 101 throw new ProtocolException(sprintf( 102 'Expected a sequence type, but received: %s', 103 get_class($attrListing) 104 )); 105 } 106 107 $attrType = $attrListing->getChild(0); 108 $vals = $attrListing->getChild(1); 109 if (!($attrType instanceof OctetStringType && $vals instanceof SetType)) { 110 throw new ProtocolException('The add request is malformed.'); 111 } 112 113 $attrValues = []; 114 foreach ($vals->getChildren() as $val) { 115 if (!$val instanceof OctetStringType) { 116 throw new ProtocolException('The add request is malformed.'); 117 } 118 $attrValues[] = $val->getValue(); 119 } 120 121 $attributes[] = new Attribute($attrType->getValue(), ...$attrValues); 122 } 123 124 return new self(new Entry($dn, ...$attributes)); 125 } 126 127 /** 128 * {@inheritdoc} 129 */ 130 public function toAsn1(): AbstractType 131 { 132 $attributeList = Asn1::sequenceOf(); 133 134 /** @var Attribute $attribute */ 135 foreach ($this->entry->getAttributes() as $attribute) { 136 $attr = Asn1::sequence(Asn1::octetString($attribute->getDescription())); 137 138 $attrValues = Asn1::setOf(...\array_map(function ($value) { 139 return Asn1::octetString($value); 140 }, $attribute->getValues())); 141 142 $attributeList->addChild($attr->addChild($attrValues)); 143 } 144 145 return Asn1::application(self::APP_TAG, Asn1::sequence( 146 Asn1::octetString($this->entry->getDn()->toString()), 147 $attributeList 148 )); 149 } 150} 151