1//
2// https://tools.ietf.org/html/rfc4511
3//
4// AddRequest ::= [APPLICATION 8] SEQUENCE {
5//      entry           LDAPDN,
6//      attributes      AttributeList }
7//
8// AttributeList ::= SEQUENCE OF attribute Attribute
9
10package ldap
11
12import (
13	"errors"
14	"log"
15
16	"gopkg.in/asn1-ber.v1"
17)
18
19// Attribute represents an LDAP attribute
20type Attribute struct {
21	// Type is the name of the LDAP attribute
22	Type string
23	// Vals are the LDAP attribute values
24	Vals []string
25}
26
27func (a *Attribute) encode() *ber.Packet {
28	seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attribute")
29	seq.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, a.Type, "Type"))
30	set := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSet, nil, "AttributeValue")
31	for _, value := range a.Vals {
32		set.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "Vals"))
33	}
34	seq.AppendChild(set)
35	return seq
36}
37
38// AddRequest represents an LDAP AddRequest operation
39type AddRequest struct {
40	// DN identifies the entry being added
41	DN string
42	// Attributes list the attributes of the new entry
43	Attributes []Attribute
44	// Controls hold optional controls to send with the request
45	Controls []Control
46}
47
48func (a AddRequest) encode() *ber.Packet {
49	request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationAddRequest, nil, "Add Request")
50	request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, a.DN, "DN"))
51	attributes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes")
52	for _, attribute := range a.Attributes {
53		attributes.AppendChild(attribute.encode())
54	}
55	request.AppendChild(attributes)
56	return request
57}
58
59// Attribute adds an attribute with the given type and values
60func (a *AddRequest) Attribute(attrType string, attrVals []string) {
61	a.Attributes = append(a.Attributes, Attribute{Type: attrType, Vals: attrVals})
62}
63
64// NewAddRequest returns an AddRequest for the given DN, with no attributes
65func NewAddRequest(dn string, controls []Control) *AddRequest {
66	return &AddRequest{
67		DN:       dn,
68		Controls: controls,
69	}
70
71}
72
73// Add performs the given AddRequest
74func (l *Conn) Add(addRequest *AddRequest) error {
75	packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
76	packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
77	packet.AppendChild(addRequest.encode())
78	if len(addRequest.Controls) > 0 {
79		packet.AppendChild(encodeControls(addRequest.Controls))
80	}
81
82	l.Debug.PrintPacket(packet)
83
84	msgCtx, err := l.sendMessage(packet)
85	if err != nil {
86		return err
87	}
88	defer l.finishMessage(msgCtx)
89
90	l.Debug.Printf("%d: waiting for response", msgCtx.id)
91	packetResponse, ok := <-msgCtx.responses
92	if !ok {
93		return NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
94	}
95	packet, err = packetResponse.ReadPacket()
96	l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
97	if err != nil {
98		return err
99	}
100
101	if l.Debug {
102		if err := addLDAPDescriptions(packet); err != nil {
103			return err
104		}
105		ber.PrintPacket(packet)
106	}
107
108	if packet.Children[1].Tag == ApplicationAddResponse {
109		err := GetLDAPError(packet)
110		if err != nil {
111			return err
112		}
113	} else {
114		log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
115	}
116
117	l.Debug.Printf("%d: returning", msgCtx.id)
118	return nil
119}
120