Updated license
[anna.git] / source / ldap / Engine.cpp
1 // ANNA - Anna is Not Nothingness Anymore
2 //
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
4 //
5 // https://bitbucket.org/testillano/anna
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 //
11 //     * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 //     * Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
16 // distribution.
17 //     * Neither the name of Google Inc. nor the names of its
18 // contributors may be used to endorse or promote products derived from
19 // this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 //
33 // Authors: eduardo.ramos.testillano@gmail.com
34 //          cisco.tierra@gmail.com
35
36
37 #include <time.h>
38 #include <signal.h>
39
40 #include <ldap.h>
41 #include <lber.h>
42
43 #include <anna/core/tracing/Logger.hpp>
44 #include <anna/core/tracing/TraceMethod.hpp>
45
46 #include <anna/xml/Node.hpp>
47
48 #include <anna/ldap/internal/sccs.hpp>
49 #include <anna/ldap/Engine.hpp>
50 #include <anna/ldap/Session.hpp>
51
52 using namespace std;
53 using namespace anna;
54 using namespace anna::ldap;
55
56 Engine::Engine() :
57   app::Component(getClassName()),
58   a_autoBind(true) {
59   ldap::sccs::activate();
60   sigset(SIGALRM, alarmnCatcher);
61 }
62
63 Session* Engine::createSession(const char* url, const char* user, const char* password, const int category)
64 throw(RuntimeException) {
65   ldap::Session* result(NULL);
66   Guard guard(this, "ldap::Engine::createSession");
67   url = completeURL(url);
68   session_iterator ii = session_find(url, (user == NULL) ? "" : user);
69
70   if(ii == session_end()) {
71     if((result = allocateSession(category)) == NULL)
72       throw RuntimeException("ldap::Engine::allocateSession returns NULL", ANNA_FILE_LOCATION);
73
74     result->a_category = category;
75     result->a_url = url;
76     result->a_externalID = -1;
77
78     if(user && *user != 0)
79       result->a_user = user;
80     else
81       result->a_user.clear();
82
83     if(password && *password != 0)
84       result->a_password = password;
85     else
86       result->a_password.clear();
87
88     session_key key(result->a_url, result->a_keymap = result->a_user);
89     a_sessions.insert(session_value_type(key, result));
90     LOGDEBUG(
91       string msg("ldap::Engine::createSession | ");
92       msg += result->asString();
93       msg += functions::asText(" | AutoBind: ", a_autoBind);
94       Logger::debug(msg, ANNA_FILE_LOCATION);
95     );
96   } else
97     result = session(ii);
98
99   if(result->getState() == Session::State::Closed && a_autoBind == true)
100     result->bind();
101
102   return result;
103 }
104
105 Session* Engine::createSession(const char* url, const int id, const char* user, const char* password, const int category)
106 throw(RuntimeException) {
107   ldap::Session* result(NULL);
108   Guard guard(this, "ldap::Engine::createSession");
109   url = completeURL(url);
110   session_iterator ii = session_find(url, id);
111
112   if(ii == session_end()) {
113     if((result = allocateSession(category)) == NULL)
114       throw RuntimeException("ldap::Engine::allocateSession returns NULL", ANNA_FILE_LOCATION);
115
116     result->a_category = category;
117     result->a_url = url;
118     result->a_externalID = id;
119
120     if(user && *user != 0)
121       result->a_user = user;
122     else
123       result->a_user.clear();
124
125     if(password && *password != 0)
126       result->a_password = password;
127     else
128       result->a_password.clear();
129
130     session_key key(result->a_url, result->a_keymap = anna::functions::asString(id));
131     a_sessions.insert(session_value_type(key, result));
132     LOGDEBUG(
133       string msg("ldap::Engine::createSession | ");
134       msg += result->asString();
135       msg += functions::asText(" | AutoBind: ", a_autoBind);
136       Logger::debug(msg, ANNA_FILE_LOCATION);
137     );
138   } else {
139     result = session(ii);
140
141     if((result->getUser() != user) || (result->getPassword() != password)) {
142       LOGWARNING(
143         std::string msg = "Returned session already existed but with different credentiales regarding provided ones. Reuse could be inappropiate.";
144         Logger::warning(msg, ANNA_FILE_LOCATION);
145       );
146     }
147   }
148
149   if(result->getState() == Session::State::Closed && a_autoBind == true)
150     result->bind();
151
152   return result;
153 }
154
155 Session* Engine::findSession(const char* url, const char* user, Exception::Mode::_v emode)
156 throw(RuntimeException) {
157   Guard guard(this, "ldap::Engine::findSession");
158   url = completeURL(url);
159   session_iterator ii = session_find(url, user);
160
161   if(ii != session_end())
162     return session(ii);
163
164   if(emode != Exception::Mode::Ignore) {
165     string msg("ldap::Engine::findSession | URL: ");
166     msg += url;
167     msg += " | User: ";
168     msg += user;
169     msg += " | Session not found";
170     RuntimeException ex(msg, ANNA_FILE_LOCATION);
171
172     if(emode == Exception::Mode::Throw)
173       throw ex;
174
175     ex.trace();
176   }
177
178   return NULL;
179 }
180
181 Session* Engine::findSession(const char* url, const int id, Exception::Mode::_v emode)
182 throw(RuntimeException) {
183   Guard guard(this, "ldap::Engine::findSession (int)");
184   session_iterator ii = session_find(url, id);
185
186   if(ii != session_end())
187     return session(ii);
188
189   if(emode != Exception::Mode::Ignore) {
190     string msg("ldap::Engine::findSession | URL: ");
191     msg += url;
192     msg += functions::asText(" | ID: ", id);
193     msg += " | Session not found";
194     RuntimeException ex(msg, ANNA_FILE_LOCATION);
195
196     if(emode == Exception::Mode::Throw)
197       throw ex;
198
199     ex.trace();
200   }
201
202   return NULL;
203 }
204
205 void Engine::closeSession(Session* session)
206 throw(RuntimeException) {
207   if(session == NULL)
208     return;
209
210   LOGDEBUG(
211     string msg("ldap::Engine::closeSession | ");
212     msg += session->asString();
213     Logger::debug(msg, ANNA_FILE_LOCATION);
214   );
215   Guard guard(this, "ldap::Engine::closeSession");
216   session_iterator ii = session_find(session->a_url, session->a_keymap);
217
218   if(ii == session_end())
219     return;
220
221   try {
222     session->unbind();
223     releaseSession(session);
224   } catch(RuntimeException& ex) {
225     ex.trace();
226   }
227
228   a_sessions.erase(ii);
229 }
230
231 void Engine::do_stop()
232 throw() {
233   LOGMETHOD(TraceMethod tttm("anna::ldap::Engine", "do_stop", ANNA_FILE_LOCATION));
234
235   for(session_iterator ii = session_begin(), maxii = session_end(); ii != maxii; ii ++)
236     session(ii)->unbind();
237 }
238
239 xml::Node* Engine::asXML(xml::Node* parent) const
240 throw() {
241   parent = app::Component::asXML(parent);
242   xml::Node* result = parent->createChild("ldap.Engine");
243   const Session* session;
244   result->createAttribute("AutoBind", functions::asString(a_autoBind));
245
246   for(const_session_iterator ii = session_begin(), maxii = session_end(); ii != maxii; ii ++) {
247     Guard guard(session = Engine::session(ii));
248     session->asXML(result);
249   }
250
251   return result;
252 }
253
254 Engine::session_iterator Engine::session_find(const char* url, const int id)
255 throw() {
256   return a_sessions.find(session_key(url, anna::functions::asString(id)));
257 }
258
259 /*static*/
260 int Engine::setDebugLevel(const int level)
261 throw(RuntimeException) {
262   int result(-1);
263 #ifdef LDAP_OPT_DEBUG_LEVEL
264   int _level = htonl(level);
265
266   if(ldap_get_option(NULL, LDAP_OPT_DEBUG_LEVEL, &result) != LDAP_OPT_SUCCESS)
267     throw RuntimeException("Can not get LDAP_OPT_DEBUG_LEVEL", ANNA_FILE_LOCATION);
268
269   if(ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &_level) != LDAP_OPT_SUCCESS)
270     throw RuntimeException("Can not set LDAP_OPT_DEBUG_LEVEL", ANNA_FILE_LOCATION);
271
272   ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &_level);
273 #else
274   Logger::error("Can not set debug level", ANNA_FILE_LOCATION);
275 #endif
276   return result;
277 }
278
279 const char* Engine::completeURL(const char* url)
280 throw() {
281   static const char* protocol = "ldap://";
282   static const int protocolLen = anna_strlen(protocol);
283
284   if(anna_strstr(url, "://") == 0) {
285     a_auxURL = protocol;
286     a_auxURL += url;
287     return a_auxURL.c_str();
288   }
289
290   return url;
291 }
292
293 // static
294 void Engine::alarmnCatcher(int)
295 throw() {
296 }