You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

173 lines
5.2 KiB

  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. package ldap
  26. import (
  27. "errors"
  28. "log"
  29. "gopkg.in/asn1-ber.v1"
  30. )
  31. // Change operation choices
  32. const (
  33. AddAttribute = 0
  34. DeleteAttribute = 1
  35. ReplaceAttribute = 2
  36. )
  37. // PartialAttribute for a ModifyRequest as defined in https://tools.ietf.org/html/rfc4511
  38. type PartialAttribute struct {
  39. // Type is the type of the partial attribute
  40. Type string
  41. // Vals are the values of the partial attribute
  42. Vals []string
  43. }
  44. func (p *PartialAttribute) encode() *ber.Packet {
  45. seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "PartialAttribute")
  46. seq.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, p.Type, "Type"))
  47. set := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSet, nil, "AttributeValue")
  48. for _, value := range p.Vals {
  49. set.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "Vals"))
  50. }
  51. seq.AppendChild(set)
  52. return seq
  53. }
  54. // Change for a ModifyRequest as defined in https://tools.ietf.org/html/rfc4511
  55. type Change struct {
  56. // Operation is the type of change to be made
  57. Operation uint
  58. // Modification is the attribute to be modified
  59. Modification PartialAttribute
  60. }
  61. func (c *Change) encode() *ber.Packet {
  62. change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change")
  63. change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(c.Operation), "Operation"))
  64. change.AppendChild(c.Modification.encode())
  65. return change
  66. }
  67. // ModifyRequest as defined in https://tools.ietf.org/html/rfc4511
  68. type ModifyRequest struct {
  69. // DN is the distinguishedName of the directory entry to modify
  70. DN string
  71. // Changes contain the attributes to modify
  72. Changes []Change
  73. // Controls hold optional controls to send with the request
  74. Controls []Control
  75. }
  76. // Add appends the given attribute to the list of changes to be made
  77. func (m *ModifyRequest) Add(attrType string, attrVals []string) {
  78. m.appendChange(AddAttribute, attrType, attrVals)
  79. }
  80. // Delete appends the given attribute to the list of changes to be made
  81. func (m *ModifyRequest) Delete(attrType string, attrVals []string) {
  82. m.appendChange(DeleteAttribute, attrType, attrVals)
  83. }
  84. // Replace appends the given attribute to the list of changes to be made
  85. func (m *ModifyRequest) Replace(attrType string, attrVals []string) {
  86. m.appendChange(ReplaceAttribute, attrType, attrVals)
  87. }
  88. func (m *ModifyRequest) appendChange(operation uint, attrType string, attrVals []string) {
  89. m.Changes = append(m.Changes, Change{operation, PartialAttribute{Type: attrType, Vals: attrVals}})
  90. }
  91. func (m ModifyRequest) encode() *ber.Packet {
  92. request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationModifyRequest, nil, "Modify Request")
  93. request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, m.DN, "DN"))
  94. changes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Changes")
  95. for _, change := range m.Changes {
  96. changes.AppendChild(change.encode())
  97. }
  98. request.AppendChild(changes)
  99. return request
  100. }
  101. // NewModifyRequest creates a modify request for the given DN
  102. func NewModifyRequest(
  103. dn string,
  104. controls []Control,
  105. ) *ModifyRequest {
  106. return &ModifyRequest{
  107. DN: dn,
  108. Controls: controls,
  109. }
  110. }
  111. // Modify performs the ModifyRequest
  112. func (l *Conn) Modify(modifyRequest *ModifyRequest) error {
  113. packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
  114. packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
  115. packet.AppendChild(modifyRequest.encode())
  116. if len(modifyRequest.Controls) > 0 {
  117. packet.AppendChild(encodeControls(modifyRequest.Controls))
  118. }
  119. l.Debug.PrintPacket(packet)
  120. msgCtx, err := l.sendMessage(packet)
  121. if err != nil {
  122. return err
  123. }
  124. defer l.finishMessage(msgCtx)
  125. l.Debug.Printf("%d: waiting for response", msgCtx.id)
  126. packetResponse, ok := <-msgCtx.responses
  127. if !ok {
  128. return NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
  129. }
  130. packet, err = packetResponse.ReadPacket()
  131. l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
  132. if err != nil {
  133. return err
  134. }
  135. if l.Debug {
  136. if err := addLDAPDescriptions(packet); err != nil {
  137. return err
  138. }
  139. ber.PrintPacket(packet)
  140. }
  141. if packet.Children[1].Tag == ApplicationModifyResponse {
  142. err := GetLDAPError(packet)
  143. if err != nil {
  144. return err
  145. }
  146. } else {
  147. log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
  148. }
  149. l.Debug.Printf("%d: returning", msgCtx.id)
  150. return nil
  151. }