1// File contains Modify functionality 2// 3// https://tools.ietf.org/html/rfc4511 4// 5// ModifyRequest ::= [APPLICATION 6] SEQUENCE { 6// object LDAPDN, 7// changes SEQUENCE OF change SEQUENCE { 8// operation ENUMERATED { 9// add (0), 10// delete (1), 11// replace (2), 12// ... }, 13// modification PartialAttribute } } 14// 15// PartialAttribute ::= SEQUENCE { 16// type AttributeDescription, 17// vals SET OF value AttributeValue } 18// 19// AttributeDescription ::= LDAPString 20// -- Constrained to <attributedescription> 21// -- [RFC4512] 22// 23// AttributeValue ::= OCTET STRING 24// 25 26package ldap 27 28import ( 29 "log" 30 31 ber "github.com/go-asn1-ber/asn1-ber" 32) 33 34// Change operation choices 35const ( 36 AddAttribute = 0 37 DeleteAttribute = 1 38 ReplaceAttribute = 2 39) 40 41// PartialAttribute for a ModifyRequest as defined in https://tools.ietf.org/html/rfc4511 42type PartialAttribute struct { 43 // Type is the type of the partial attribute 44 Type string 45 // Vals are the values of the partial attribute 46 Vals []string 47} 48 49func (p *PartialAttribute) encode() *ber.Packet { 50 seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "PartialAttribute") 51 seq.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, p.Type, "Type")) 52 set := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSet, nil, "AttributeValue") 53 for _, value := range p.Vals { 54 set.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "Vals")) 55 } 56 seq.AppendChild(set) 57 return seq 58} 59 60// Change for a ModifyRequest as defined in https://tools.ietf.org/html/rfc4511 61type Change struct { 62 // Operation is the type of change to be made 63 Operation uint 64 // Modification is the attribute to be modified 65 Modification PartialAttribute 66} 67 68func (c *Change) encode() *ber.Packet { 69 change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change") 70 change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(c.Operation), "Operation")) 71 change.AppendChild(c.Modification.encode()) 72 return change 73} 74 75// ModifyRequest as defined in https://tools.ietf.org/html/rfc4511 76type ModifyRequest struct { 77 // DN is the distinguishedName of the directory entry to modify 78 DN string 79 // Changes contain the attributes to modify 80 Changes []Change 81 // Controls hold optional controls to send with the request 82 Controls []Control 83} 84 85// Add appends the given attribute to the list of changes to be made 86func (req *ModifyRequest) Add(attrType string, attrVals []string) { 87 req.appendChange(AddAttribute, attrType, attrVals) 88} 89 90// Delete appends the given attribute to the list of changes to be made 91func (req *ModifyRequest) Delete(attrType string, attrVals []string) { 92 req.appendChange(DeleteAttribute, attrType, attrVals) 93} 94 95// Replace appends the given attribute to the list of changes to be made 96func (req *ModifyRequest) Replace(attrType string, attrVals []string) { 97 req.appendChange(ReplaceAttribute, attrType, attrVals) 98} 99 100func (req *ModifyRequest) appendChange(operation uint, attrType string, attrVals []string) { 101 req.Changes = append(req.Changes, Change{operation, PartialAttribute{Type: attrType, Vals: attrVals}}) 102} 103 104func (req *ModifyRequest) appendTo(envelope *ber.Packet) error { 105 pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationModifyRequest, nil, "Modify Request") 106 pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.DN, "DN")) 107 changes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Changes") 108 for _, change := range req.Changes { 109 changes.AppendChild(change.encode()) 110 } 111 pkt.AppendChild(changes) 112 113 envelope.AppendChild(pkt) 114 if len(req.Controls) > 0 { 115 envelope.AppendChild(encodeControls(req.Controls)) 116 } 117 118 return nil 119} 120 121// NewModifyRequest creates a modify request for the given DN 122func NewModifyRequest(dn string, controls []Control) *ModifyRequest { 123 return &ModifyRequest{ 124 DN: dn, 125 Controls: controls, 126 } 127} 128 129// Modify performs the ModifyRequest 130func (l *Conn) Modify(modifyRequest *ModifyRequest) error { 131 msgCtx, err := l.doRequest(modifyRequest) 132 if err != nil { 133 return err 134 } 135 defer l.finishMessage(msgCtx) 136 137 packet, err := l.readPacket(msgCtx) 138 if err != nil { 139 return err 140 } 141 142 if packet.Children[1].Tag == ApplicationModifyResponse { 143 err := GetLDAPError(packet) 144 if err != nil { 145 return err 146 } 147 } else { 148 log.Printf("Unexpected Response: %d", packet.Children[1].Tag) 149 } 150 return nil 151} 152