File: ldifProducer.py

package info (click to toggle)
python-ldap3 0.9.9.3-1~bpo8%2B2
  • links: PTS, VCS
  • area: main
  • in suites: jessie-backports
  • size: 2,316 kB
  • sloc: python: 19,119; makefile: 3
file content (135 lines) | stat: -rw-r--r-- 5,045 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
"""
"""

# Created on 2013.07.15
#
# Author: Giovanni Cannata
#
# Copyright 2015 Giovanni Cannata
#
# This file is part of ldap3.
#
# ldap3 is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ldap3 is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with ldap3 in the COPYING and COPYING.LESSER files.
# If not, see <http://www.gnu.org/licenses/>.

from io import StringIO
from os import linesep
import random

from .. import LDAP_MAX_INT
from ..core.exceptions import LDAPLDIFError
from ..utils.conv import prepare_for_stream
from ..protocol.rfc4511 import LDAPMessage, MessageID, ProtocolOp
from ..protocol.rfc2849 import operation_to_ldif, add_ldif_header
from ..protocol.convert import build_controls_list
from .base import BaseStrategy


class LdifProducerStrategy(BaseStrategy):
    """
    This strategy is used to create the LDIF stream for the Add, Delete, Modify, ModifyDn operations.
    You send the request and get the request in the ldif-change representation of the operation.
    NO OPERATION IS SENT TO THE LDAP SERVER!
    Connection.request will contain the result LDAP message in a dict form
    Connection.response will contain the ldif-change format of the requested operation if available
    You don't need a real server to connect to for this strategy
    """

    def __init__(self, ldap_connection):
        BaseStrategy.__init__(self, ldap_connection)
        self.sync = True
        self.no_real_dsa = True
        self.pooled = False
        self.can_stream = True
        self.line_separator = linesep
        self.all_base64 = False
        self.stream = None
        self.order = dict()
        self._header_added = False
        random.seed()

    def _start_listen(self):
        self.connection.listening = True
        self.connection.closed = False
        self._header_added = False
        if not self.stream or (isinstance(self.stream, StringIO) and self.stream.closed):
            self.set_stream(StringIO())

    def _stop_listen(self):
        self.stream.close()
        self.connection.listening = False
        self.connection.closed = True

    def receiving(self):
        return None

    def send(self, message_type, request, controls=None):
        """
        Build the LDAPMessage without sending to server
        """
        message_id = random.randint(0, LDAP_MAX_INT)
        ldap_message = LDAPMessage()
        ldap_message['messageID'] = MessageID(message_id)
        ldap_message['protocolOp'] = ProtocolOp().setComponentByName(message_type, request)
        message_controls = build_controls_list(controls)
        if message_controls is not None:
            ldap_message['controls'] = message_controls

        self.connection.request = BaseStrategy.decode_request(ldap_message)
        self.connection.request['controls'] = controls
        self._outstanding[message_id] = self.connection.request
        return message_id

    def post_send_single_response(self, message_id):
        self.connection.response = None
        self.connection.result = None
        if self._outstanding and message_id in self._outstanding:
            request = self._outstanding.pop(message_id)
            ldif_lines = operation_to_ldif(self.connection.request['type'], request, self.all_base64, self.order.get(self.connection.request['type']))
            if self.stream and ldif_lines and not self.connection.closed:
                self.accumulate_stream(self.line_separator.join(ldif_lines))
            ldif_lines = add_ldif_header(ldif_lines)
            self.connection.response = self.line_separator.join(ldif_lines)
            return self.connection.response

        return None

    def post_send_search(self, message_id):
        raise LDAPLDIFError('LDIF-CONTENT cannot be produced for Search Operations')

    def _get_response(self, message_id):
        pass

    def accumulate_stream(self, fragment):
        if not self._header_added and self.stream.tell() == 0:
            self._header_added = True
            header = add_ldif_header(['-'])[0]
            self.stream.write(prepare_for_stream(header + self.line_separator + self.line_separator))
        self.stream.write(prepare_for_stream(fragment + self.line_separator + self.line_separator))

    def get_stream(self):
        return self.stream

    def set_stream(self, value):
        error = False
        try:
            if not value.writable():
                error = True
        except (ValueError, AttributeError):
            error = True

        if error:
            raise LDAPLDIFError('stream must be writable')

        self.stream = value