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.

170 lines
5.8 KiB

  1. // Copyright 2014 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. //
  5. // File contains Modify functionality
  6. //
  7. // https://tools.ietf.org/html/rfc4511
  8. //
  9. // ModifyRequest ::= [APPLICATION 6] SEQUENCE {
  10. // object LDAPDN,
  11. // changes SEQUENCE OF change SEQUENCE {
  12. // operation ENUMERATED {
  13. // add (0),
  14. // delete (1),
  15. // replace (2),
  16. // ... },
  17. // modification PartialAttribute } }
  18. //
  19. // PartialAttribute ::= SEQUENCE {
  20. // type AttributeDescription,
  21. // vals SET OF value AttributeValue }
  22. //
  23. // AttributeDescription ::= LDAPString
  24. // -- Constrained to <attributedescription>
  25. // -- [RFC4512]
  26. //
  27. // AttributeValue ::= OCTET STRING
  28. //
  29. package ldap
  30. import (
  31. "errors"
  32. "log"
  33. "gopkg.in/asn1-ber.v1"
  34. )
  35. // Change operation choices
  36. const (
  37. AddAttribute = 0
  38. DeleteAttribute = 1
  39. ReplaceAttribute = 2
  40. )
  41. // PartialAttribute for a ModifyRequest as defined in https://tools.ietf.org/html/rfc4511
  42. type 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. func (p *PartialAttribute) encode() *ber.Packet {
  49. seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "PartialAttribute")
  50. seq.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, p.Type, "Type"))
  51. set := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSet, nil, "AttributeValue")
  52. for _, value := range p.Vals {
  53. set.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "Vals"))
  54. }
  55. seq.AppendChild(set)
  56. return seq
  57. }
  58. // ModifyRequest as defined in https://tools.ietf.org/html/rfc4511
  59. type ModifyRequest struct {
  60. // DN is the distinguishedName of the directory entry to modify
  61. DN string
  62. // AddAttributes contain the attributes to add
  63. AddAttributes []PartialAttribute
  64. // DeleteAttributes contain the attributes to delete
  65. DeleteAttributes []PartialAttribute
  66. // ReplaceAttributes contain the attributes to replace
  67. ReplaceAttributes []PartialAttribute
  68. }
  69. // Add inserts the given attribute to the list of attributes to add
  70. func (m *ModifyRequest) Add(attrType string, attrVals []string) {
  71. m.AddAttributes = append(m.AddAttributes, PartialAttribute{Type: attrType, Vals: attrVals})
  72. }
  73. // Delete inserts the given attribute to the list of attributes to delete
  74. func (m *ModifyRequest) Delete(attrType string, attrVals []string) {
  75. m.DeleteAttributes = append(m.DeleteAttributes, PartialAttribute{Type: attrType, Vals: attrVals})
  76. }
  77. // Replace inserts the given attribute to the list of attributes to replace
  78. func (m *ModifyRequest) Replace(attrType string, attrVals []string) {
  79. m.ReplaceAttributes = append(m.ReplaceAttributes, PartialAttribute{Type: attrType, Vals: attrVals})
  80. }
  81. func (m ModifyRequest) encode() *ber.Packet {
  82. request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationModifyRequest, nil, "Modify Request")
  83. request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, m.DN, "DN"))
  84. changes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Changes")
  85. for _, attribute := range m.AddAttributes {
  86. change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change")
  87. change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(AddAttribute), "Operation"))
  88. change.AppendChild(attribute.encode())
  89. changes.AppendChild(change)
  90. }
  91. for _, attribute := range m.DeleteAttributes {
  92. change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change")
  93. change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(DeleteAttribute), "Operation"))
  94. change.AppendChild(attribute.encode())
  95. changes.AppendChild(change)
  96. }
  97. for _, attribute := range m.ReplaceAttributes {
  98. change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change")
  99. change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(ReplaceAttribute), "Operation"))
  100. change.AppendChild(attribute.encode())
  101. changes.AppendChild(change)
  102. }
  103. request.AppendChild(changes)
  104. return request
  105. }
  106. // NewModifyRequest creates a modify request for the given DN
  107. func NewModifyRequest(
  108. dn string,
  109. ) *ModifyRequest {
  110. return &ModifyRequest{
  111. DN: dn,
  112. }
  113. }
  114. // Modify performs the ModifyRequest
  115. func (l *Conn) Modify(modifyRequest *ModifyRequest) error {
  116. packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
  117. packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
  118. packet.AppendChild(modifyRequest.encode())
  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. resultCode, resultDescription := getLDAPResultCode(packet)
  143. if resultCode != 0 {
  144. return NewError(resultCode, errors.New(resultDescription))
  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. }