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}
45
46func (a AddRequest) encode() *ber.Packet {
47	request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationAddRequest, nil, "Add Request")
48	request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, a.DN, "DN"))
49	attributes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes")
50	for _, attribute := range a.Attributes {
51		attributes.AppendChild(attribute.encode())
52	}
53	request.AppendChild(attributes)
54	return request
55}
56
57// Attribute adds an attribute with the given type and values
58func (a *AddRequest) Attribute(attrType string, attrVals []string) {
59	a.Attributes = append(a.Attributes, Attribute{Type: attrType, Vals: attrVals})
60}
61
62// NewAddRequest returns an AddRequest for the given DN, with no attributes
63func NewAddRequest(dn string) *AddRequest {
64	return &AddRequest{
65		DN: dn,
66	}
67
68}
69
70// Add performs the given AddRequest
71func (l *Conn) Add(addRequest *AddRequest) error {
72	packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
73	packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
74	packet.AppendChild(addRequest.encode())
75
76	l.Debug.PrintPacket(packet)
77
78	msgCtx, err := l.sendMessage(packet)
79	if err != nil {
80		return err
81	}
82	defer l.finishMessage(msgCtx)
83
84	l.Debug.Printf("%d: waiting for response", msgCtx.id)
85	packetResponse, ok := <-msgCtx.responses
86	if !ok {
87		return NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
88	}
89	packet, err = packetResponse.ReadPacket()
90	l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
91	if err != nil {
92		return err
93	}
94
95	if l.Debug {
96		if err := addLDAPDescriptions(packet); err != nil {
97			return err
98		}
99		ber.PrintPacket(packet)
100	}
101
102	if packet.Children[1].Tag == ApplicationAddResponse {
103		resultCode, resultDescription := getLDAPResultCode(packet)
104		if resultCode != 0 {
105			return NewError(resultCode, errors.New(resultDescription))
106		}
107	} else {
108		log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
109	}
110
111	l.Debug.Printf("%d: returning", msgCtx.id)
112	return nil
113}
114