modify.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  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. }
  74. // Add appends the given attribute to the list of changes to be made
  75. func (m *ModifyRequest) Add(attrType string, attrVals []string) {
  76. m.appendChange(AddAttribute, attrType, attrVals)
  77. }
  78. // Delete appends the given attribute to the list of changes to be made
  79. func (m *ModifyRequest) Delete(attrType string, attrVals []string) {
  80. m.appendChange(DeleteAttribute, attrType, attrVals)
  81. }
  82. // Replace appends the given attribute to the list of changes to be made
  83. func (m *ModifyRequest) Replace(attrType string, attrVals []string) {
  84. m.appendChange(ReplaceAttribute, attrType, attrVals)
  85. }
  86. func (m *ModifyRequest) appendChange(operation uint, attrType string, attrVals []string) {
  87. m.Changes = append(m.Changes, Change{operation, PartialAttribute{Type: attrType, Vals: attrVals}})
  88. }
  89. func (m ModifyRequest) encode() *ber.Packet {
  90. request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationModifyRequest, nil, "Modify Request")
  91. request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, m.DN, "DN"))
  92. changes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Changes")
  93. for _, change := range m.Changes {
  94. changes.AppendChild(change.encode())
  95. }
  96. request.AppendChild(changes)
  97. return request
  98. }
  99. // NewModifyRequest creates a modify request for the given DN
  100. func NewModifyRequest(
  101. dn string,
  102. ) *ModifyRequest {
  103. return &ModifyRequest{
  104. DN: dn,
  105. }
  106. }
  107. // Modify performs the ModifyRequest
  108. func (l *Conn) Modify(modifyRequest *ModifyRequest) error {
  109. packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
  110. packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
  111. packet.AppendChild(modifyRequest.encode())
  112. l.Debug.PrintPacket(packet)
  113. msgCtx, err := l.sendMessage(packet)
  114. if err != nil {
  115. return err
  116. }
  117. defer l.finishMessage(msgCtx)
  118. l.Debug.Printf("%d: waiting for response", msgCtx.id)
  119. packetResponse, ok := <-msgCtx.responses
  120. if !ok {
  121. return NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
  122. }
  123. packet, err = packetResponse.ReadPacket()
  124. l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
  125. if err != nil {
  126. return err
  127. }
  128. if l.Debug {
  129. if err := addLDAPDescriptions(packet); err != nil {
  130. return err
  131. }
  132. ber.PrintPacket(packet)
  133. }
  134. if packet.Children[1].Tag == ApplicationModifyResponse {
  135. resultCode, resultDescription := getLDAPResultCode(packet)
  136. if resultCode != 0 {
  137. return NewError(resultCode, errors.New(resultDescription))
  138. }
  139. } else {
  140. log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
  141. }
  142. l.Debug.Printf("%d: returning", msgCtx.id)
  143. return nil
  144. }