bdf29dff3fab86aba7002d8f546c0f322a5ab7c3
[public/netxms.git] / src / server / core / userdb_objects.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2012 Victor Kirhenshtein
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 ** File: userdb_objects.cpp
20 **/
21
22 #include "nxcore.h"
23
24 /**
25 * Generate hash for password
26 */
27 static void CalculatePasswordHash(const TCHAR *password, PasswordHashType type, PasswordHash *ph, const BYTE *salt = NULL)
28 {
29 #ifdef UNICODE
30 char mbPassword[1024];
31 WideCharToMultiByte(CP_UTF8, 0, password, -1, mbPassword, 1024, NULL, NULL);
32 mbPassword[1023] = 0;
33 #else
34 const char *mbPassword = password;
35 #endif
36
37 BYTE buffer[1024];
38
39 memset(ph, 0, sizeof(PasswordHash));
40 ph->hashType = type;
41 switch(type)
42 {
43 case PWD_HASH_SHA1:
44 CalculateSHA1Hash((BYTE *)mbPassword, strlen(mbPassword), ph->hash);
45 break;
46 case PWD_HASH_SHA256:
47 if (salt != NULL)
48 memcpy(buffer, salt, PASSWORD_SALT_LENGTH);
49 else
50 GenerateRandomBytes(buffer, PASSWORD_SALT_LENGTH);
51 strcpy((char *)&buffer[PASSWORD_SALT_LENGTH], mbPassword);
52 CalculateSHA256Hash(buffer, strlen(mbPassword) + PASSWORD_SALT_LENGTH, ph->hash);
53 memcpy(ph->salt, buffer, PASSWORD_SALT_LENGTH);
54 break;
55 default:
56 break;
57 }
58 }
59
60 /*****************************************************************************
61 ** UserDatabaseObject
62 ****************************************************************************/
63
64 /**
65 * Constructor for generic user database object - create from database
66 * Expects fields in the following order:
67 * id,name,system_access,flags,description,guid,ldap_dn
68 */
69 UserDatabaseObject::UserDatabaseObject(DB_RESULT hResult, int row)
70 {
71 m_id = DBGetFieldULong(hResult, row, 0);
72 DBGetField(hResult, row, 1, m_name, MAX_USER_NAME);
73 m_systemRights = DBGetFieldUInt64(hResult, row, 2);
74 m_flags = DBGetFieldULong(hResult, row, 3);
75 DBGetField(hResult, row, 4, m_description, MAX_USER_DESCR);
76 DBGetFieldGUID(hResult, row, 5, m_guid);
77 m_userDn = DBGetField(hResult, row, 6, NULL, 0);
78 }
79
80 /**
81 * Default constructor for generic object
82 */
83 UserDatabaseObject::UserDatabaseObject()
84 {
85 m_userDn = NULL;
86 }
87
88 /**
89 * Constructor for generic object - create new object with given id and name
90 */
91 UserDatabaseObject::UserDatabaseObject(UINT32 id, const TCHAR *name)
92 {
93 m_id = id;
94 uuid_generate(m_guid);
95 nx_strncpy(m_name, name, MAX_USER_NAME);
96 m_systemRights = 0;
97 m_description[0] = 0;
98 m_flags = UF_MODIFIED;
99 m_userDn = NULL;
100 }
101
102 /**
103 * Destructor for generic user database object
104 */
105 UserDatabaseObject::~UserDatabaseObject()
106 {
107 safe_free(m_userDn);
108 }
109
110 /**
111 * Save object to database
112 */
113 bool UserDatabaseObject::saveToDatabase(DB_HANDLE hdb)
114 {
115 return false;
116 }
117
118 /**
119 * Delete object from database
120 */
121 bool UserDatabaseObject::deleteFromDatabase(DB_HANDLE hdb)
122 {
123 return false;
124 }
125
126 /**
127 * Fill NXCP message with object data
128 */
129 void UserDatabaseObject::fillMessage(NXCPMessage *msg)
130 {
131 msg->setField(VID_USER_ID, m_id);
132 msg->setField(VID_USER_NAME, m_name);
133 msg->setField(VID_USER_FLAGS, (WORD)m_flags);
134 msg->setField(VID_USER_SYS_RIGHTS, m_systemRights);
135 msg->setField(VID_USER_DESCRIPTION, m_description);
136 msg->setField(VID_GUID, m_guid, UUID_LENGTH);
137 m_attributes.fillMessage(msg, VID_NUM_CUSTOM_ATTRIBUTES, VID_CUSTOM_ATTRIBUTES_BASE);
138 }
139
140 /**
141 * Modify object from NXCP message
142 */
143 void UserDatabaseObject::modifyFromMessage(NXCPMessage *msg)
144 {
145 UINT32 flags, fields;
146
147 fields = msg->getFieldAsUInt32(VID_FIELDS);
148 DbgPrintf(5, _T("UserDatabaseObject::modifyFromMessage(): id=%d fields=%08X"), m_id, fields);
149
150 if (fields & USER_MODIFY_DESCRIPTION)
151 msg->getFieldAsString(VID_USER_DESCRIPTION, m_description, MAX_USER_DESCR);
152 if (fields & USER_MODIFY_LOGIN_NAME)
153 msg->getFieldAsString(VID_USER_NAME, m_name, MAX_USER_NAME);
154
155 // Update custom attributes only if VID_NUM_CUSTOM_ATTRIBUTES exist -
156 // older client versions may not be aware of custom attributes
157 if ((fields & USER_MODIFY_CUSTOM_ATTRIBUTES) || msg->isFieldExist(VID_NUM_CUSTOM_ATTRIBUTES))
158 {
159 UINT32 i, varId, count;
160 TCHAR *name, *value;
161
162 count = msg->getFieldAsUInt32(VID_NUM_CUSTOM_ATTRIBUTES);
163 m_attributes.clear();
164 for(i = 0, varId = VID_CUSTOM_ATTRIBUTES_BASE; i < count; i++)
165 {
166 name = msg->getFieldAsString(varId++);
167 value = msg->getFieldAsString(varId++);
168 m_attributes.setPreallocated((name != NULL) ? name : _tcsdup(_T("")), (value != NULL) ? value : _tcsdup(_T("")));
169 }
170 }
171
172 if ((m_id != 0) && (fields & USER_MODIFY_ACCESS_RIGHTS))
173 m_systemRights = msg->getFieldAsUInt64(VID_USER_SYS_RIGHTS);
174
175 if (fields & USER_MODIFY_FLAGS)
176 {
177 flags = msg->getFieldAsUInt16(VID_USER_FLAGS);
178 //Null dn if user is detached from LDAP
179 if((m_flags & UF_LDAP_USER) && !(flags & UF_LDAP_USER))
180 setDn(NULL);
181 // Modify only UF_DISABLED, UF_CHANGE_PASSWORD, UF_CANNOT_CHANGE_PASSWORD and UF_LDAP_USER flags from message
182 // Ignore all but CHANGE_PASSWORD flag for superuser and "everyone" group
183 m_flags &= ~(UF_DISABLED | UF_CHANGE_PASSWORD | UF_CANNOT_CHANGE_PASSWORD | UF_LDAP_USER);
184 if ((m_id == 0) || (m_id == GROUP_EVERYONE))
185 m_flags |= flags & UF_CHANGE_PASSWORD;
186 else
187 m_flags |= flags & (UF_DISABLED | UF_CHANGE_PASSWORD | UF_CANNOT_CHANGE_PASSWORD | UF_LDAP_USER);
188
189 }
190
191 m_flags |= UF_MODIFIED;
192 }
193
194 /**
195 * Load custom attributes from database
196 */
197 bool UserDatabaseObject::loadCustomAttributes(DB_HANDLE hdb)
198 {
199 DB_RESULT hResult;
200 TCHAR query[256], *attrName, *attrValue;
201 bool success = false;
202
203 _sntprintf(query, 256, _T("SELECT attr_name,attr_value FROM userdb_custom_attributes WHERE object_id=%d"), m_id);
204 hResult = DBSelect(hdb, query);
205 if (hResult != NULL)
206 {
207 int count = DBGetNumRows(hResult);
208 for(int i = 0; i < count; i++)
209 {
210 attrName = DBGetField(hResult, i, 0, NULL, 0);
211 if (attrName == NULL)
212 attrName = _tcsdup(_T(""));
213
214 attrValue = DBGetField(hResult, i, 1, NULL, 0);
215 if (attrValue == NULL)
216 attrValue = _tcsdup(_T(""));
217
218 m_attributes.setPreallocated(attrName, attrValue);
219 }
220 DBFreeResult(hResult);
221 success = true;
222 }
223 return success;
224 }
225
226 /**
227 * Callback for saving custom attribute in database
228 */
229 static EnumerationCallbackResult SaveAttributeCallback(const TCHAR *key, const void *value, void *data)
230 {
231 DB_STATEMENT hStmt = (DB_STATEMENT)data;
232 DBBind(hStmt, 2, DB_SQLTYPE_VARCHAR, key, DB_BIND_STATIC);
233 DBBind(hStmt, 3, DB_SQLTYPE_VARCHAR, (const TCHAR *)value, DB_BIND_STATIC);
234 return DBExecute(hStmt) ? _CONTINUE : _STOP;
235 }
236
237 /**
238 * Save custom attributes to database
239 */
240 bool UserDatabaseObject::saveCustomAttributes(DB_HANDLE hdb)
241 {
242 TCHAR query[256];
243 bool success = false;
244
245 _sntprintf(query, 256, _T("DELETE FROM userdb_custom_attributes WHERE object_id=%d"), m_id);
246 if (DBQuery(hdb, query))
247 {
248 DB_STATEMENT hStmt = DBPrepare(hdb, _T("INSERT INTO userdb_custom_attributes (object_id,attr_name,attr_value) VALUES (?,?,?)"));
249 if (hStmt != NULL)
250 {
251 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
252 success = (m_attributes.forEach(SaveAttributeCallback, hStmt) == _CONTINUE);
253 DBFreeStatement(hStmt);
254 }
255 }
256 return success;
257 }
258
259 /**
260 * Get custom attribute as UINT32
261 */
262 UINT32 UserDatabaseObject::getAttributeAsULong(const TCHAR *name)
263 {
264 const TCHAR *value = getAttribute(name);
265 return (value != NULL) ? _tcstoul(value, NULL, 0) : 0;
266 }
267
268 void UserDatabaseObject::setDescription(const TCHAR *description)
269 {
270 if (_tcscmp(CHECK_NULL_EX(m_description), CHECK_NULL_EX(description)))
271 {
272 nx_strncpy(m_description, CHECK_NULL_EX(description), MAX_USER_DESCR);
273 m_flags |= UF_MODIFIED;
274 }
275 }
276
277 void UserDatabaseObject::setName(const TCHAR *name)
278 {
279 if (_tcscmp(CHECK_NULL_EX(m_name), CHECK_NULL_EX(name)))
280 {
281 nx_strncpy(m_name, CHECK_NULL_EX(name), MAX_USER_NAME);
282 m_flags |= UF_MODIFIED;
283 }
284 }
285
286 void UserDatabaseObject::setDn(const TCHAR *dn)
287 {
288 safe_free(m_userDn);
289 m_userDn = (dn != NULL) ? _tcsdup(dn) : NULL;
290 m_flags |= UF_MODIFIED;
291 }
292
293 /**
294 * Disable user account because of sync exception
295 */
296 void UserDatabaseObject::setSyncException()
297 {
298 m_flags |= UF_SYNC_EXCEPTION | UF_MODIFIED;
299 SendUserDBUpdate(USER_DB_MODIFY, m_id, this);
300 }
301
302 void UserDatabaseObject::removeSyncException()
303 {
304 if((m_flags & UF_SYNC_EXCEPTION)> 0)
305 {
306 m_flags &= ~UF_SYNC_EXCEPTION;
307 m_flags |= UF_MODIFIED;
308 }
309 }
310
311 /**
312 * Enable user account
313 */
314 void UserDatabaseObject::enable()
315 {
316 m_flags &= ~(UF_DISABLED);
317 m_flags |= UF_MODIFIED;
318 SendUserDBUpdate(USER_DB_MODIFY, m_id, this);
319 }
320
321 /**
322 * Disable user account
323 */
324 void UserDatabaseObject::disable()
325 {
326 m_flags |= UF_DISABLED | UF_MODIFIED;
327 SendUserDBUpdate(USER_DB_MODIFY, m_id, this);
328 }
329
330 /*****************************************************************************
331 ** User
332 ****************************************************************************/
333
334 /**
335 * Constructor for user object - create from database
336 * Expects fields in the following order:
337 * id,name,system_access,flags,description,guid,ldap_dn,password,full_name,
338 * grace_logins,auth_method,cert_mapping_method,cert_mapping_data,
339 * auth_failures,last_passwd_change,min_passwd_length,disabled_until,
340 * last_login,xmpp_id
341 */
342 User::User(DB_RESULT hResult, int row) : UserDatabaseObject(hResult, row)
343 {
344 TCHAR buffer[256];
345
346 bool validHash = false;
347 DBGetField(hResult, row, 7, buffer, 256);
348 if (buffer[0] == _T('$'))
349 {
350 // new format - with hash type indicator
351 if (buffer[1] == 'A')
352 {
353 m_password.hashType = PWD_HASH_SHA256;
354 if (_tcslen(buffer) >= 82)
355 {
356 if ((StrToBin(&buffer[2], m_password.salt, PASSWORD_SALT_LENGTH) == PASSWORD_SALT_LENGTH) &&
357 (StrToBin(&buffer[18], m_password.hash, SHA256_DIGEST_SIZE) == SHA256_DIGEST_SIZE))
358 validHash = true;
359 }
360 }
361 }
362 else
363 {
364 // old format - SHA1 hash without salt
365 m_password.hashType = PWD_HASH_SHA1;
366 if (StrToBin(buffer, m_password.hash, SHA1_DIGEST_SIZE) == SHA1_DIGEST_SIZE)
367 validHash = true;
368 }
369 if (!validHash)
370 {
371 nxlog_write(MSG_INVALID_PASSWORD_HASH, NXLOG_WARNING, "s", m_name);
372 CalculatePasswordHash(_T("netxms"), PWD_HASH_SHA256, &m_password);
373 m_flags |= UF_MODIFIED | UF_CHANGE_PASSWORD;
374 }
375
376 DBGetField(hResult, row, 8, m_fullName, MAX_USER_FULLNAME);
377 m_graceLogins = DBGetFieldLong(hResult, row, 9);
378 m_authMethod = DBGetFieldLong(hResult, row, 10);
379 m_certMappingMethod = DBGetFieldLong(hResult, row, 11);
380 m_certMappingData = DBGetField(hResult, row, 12, NULL, 0);
381 m_authFailures = DBGetFieldLong(hResult, row, 13);
382 m_lastPasswordChange = (time_t)DBGetFieldLong(hResult, row, 14);
383 m_minPasswordLength = DBGetFieldLong(hResult, row, 15);
384 m_disabledUntil = (time_t)DBGetFieldLong(hResult, row, 16);
385 m_lastLogin = (time_t)DBGetFieldLong(hResult, row, 17);
386 DBGetField(hResult, row, 18, m_xmppId, MAX_XMPP_ID_LEN);
387
388 // Set full system access for superuser
389 if (m_id == 0)
390 m_systemRights = SYSTEM_ACCESS_FULL;
391
392 loadCustomAttributes(g_hCoreDB);
393 }
394
395 /**
396 * Constructor for user object - create default superuser
397 */
398 User::User()
399 {
400 m_id = 0;
401 _tcscpy(m_name, _T("admin"));
402 m_flags = UF_MODIFIED | UF_CHANGE_PASSWORD;
403 m_systemRights = SYSTEM_ACCESS_FULL;
404 m_fullName[0] = 0;
405 _tcscpy(m_description, _T("Built-in system administrator account"));
406 CalculatePasswordHash(_T("netxms"), PWD_HASH_SHA256, &m_password);
407 m_graceLogins = MAX_GRACE_LOGINS;
408 m_authMethod = AUTH_NETXMS_PASSWORD;
409 uuid_generate(m_guid);
410 m_certMappingMethod = USER_MAP_CERT_BY_CN;
411 m_certMappingData = NULL;
412 m_authFailures = 0;
413 m_lastPasswordChange = 0;
414 m_minPasswordLength = -1; // Use system-wide default
415 m_disabledUntil = 0;
416 m_lastLogin = 0;
417 m_xmppId[0] = 0;
418
419 }
420
421 /**
422 * Constructor for user object - new user
423 */
424 User::User(UINT32 id, const TCHAR *name) : UserDatabaseObject(id, name)
425 {
426 m_fullName[0] = 0;
427 m_graceLogins = MAX_GRACE_LOGINS;
428 m_authMethod = AUTH_NETXMS_PASSWORD;
429 m_certMappingMethod = USER_MAP_CERT_BY_CN;
430 m_certMappingData = NULL;
431 CalculatePasswordHash(_T(""), PWD_HASH_SHA256, &m_password);
432 m_authFailures = 0;
433 m_lastPasswordChange = 0;
434 m_minPasswordLength = -1; // Use system-wide default
435 m_disabledUntil = 0;
436 m_lastLogin = 0;
437 m_xmppId[0] = 0;
438 }
439
440 /**
441 * Destructor for user object
442 */
443 User::~User()
444 {
445 safe_free(m_certMappingData);
446 }
447
448 /**
449 * Save object to database
450 */
451 bool User::saveToDatabase(DB_HANDLE hdb)
452 {
453 TCHAR password[128], guidText[64];
454
455 // Clear modification flag
456 m_flags &= ~UF_MODIFIED;
457
458 // Create or update record in database
459 switch(m_password.hashType)
460 {
461 case PWD_HASH_SHA1:
462 BinToStr(m_password.hash, SHA1_DIGEST_SIZE, password);
463 break;
464 case PWD_HASH_SHA256:
465 _tcscpy(password, _T("$A"));
466 BinToStr(m_password.salt, PASSWORD_SALT_LENGTH, &password[2]);
467 BinToStr(m_password.hash, SHA256_DIGEST_SIZE, &password[18]);
468 break;
469 default:
470 _tcscpy(password, _T("$$"));
471 break;
472 }
473 DB_STATEMENT hStmt;
474 if (IsDatabaseRecordExist(hdb, _T("users"), _T("id"), m_id))
475 {
476 hStmt = DBPrepare(hdb,
477 _T("UPDATE users SET name=?,password=?,system_access=?,flags=?,full_name=?,description=?,grace_logins=?,guid=?,")
478 _T(" auth_method=?,cert_mapping_method=?,cert_mapping_data=?,auth_failures=?,last_passwd_change=?,")
479 _T(" min_passwd_length=?,disabled_until=?,last_login=?,xmpp_id=?,ldap_dn=? WHERE id=?"));
480 }
481 else
482 {
483 hStmt = DBPrepare(hdb,
484 _T("INSERT INTO users (name,password,system_access,flags,full_name,description,grace_logins,guid,auth_method,")
485 _T(" cert_mapping_method,cert_mapping_data,password_history,auth_failures,last_passwd_change,min_passwd_length,")
486 _T(" disabled_until,last_login,xmpp_id,ldap_dn,id) VALUES (?,?,?,?,?,?,?,?,?,?,?,'',?,?,?,?,?,?,?,?)"));
487 }
488 if (hStmt == NULL)
489 return false;
490
491 DBBind(hStmt, 1, DB_SQLTYPE_VARCHAR, m_name, DB_BIND_STATIC);
492 DBBind(hStmt, 2, DB_SQLTYPE_VARCHAR, password, DB_BIND_STATIC);
493 DBBind(hStmt, 3, DB_SQLTYPE_BIGINT, m_systemRights);
494 DBBind(hStmt, 4, DB_SQLTYPE_INTEGER, m_flags);
495 DBBind(hStmt, 5, DB_SQLTYPE_VARCHAR, m_fullName, DB_BIND_STATIC);
496 DBBind(hStmt, 6, DB_SQLTYPE_VARCHAR, m_description, DB_BIND_STATIC);
497 DBBind(hStmt, 7, DB_SQLTYPE_INTEGER, m_graceLogins);
498 DBBind(hStmt, 8, DB_SQLTYPE_VARCHAR, uuid_to_string(m_guid, guidText), DB_BIND_STATIC);
499 DBBind(hStmt, 9, DB_SQLTYPE_INTEGER, m_authMethod);
500 DBBind(hStmt, 10, DB_SQLTYPE_INTEGER, m_certMappingMethod);
501 DBBind(hStmt, 11, DB_SQLTYPE_VARCHAR, m_certMappingData, DB_BIND_STATIC);
502 DBBind(hStmt, 12, DB_SQLTYPE_INTEGER, m_authFailures);
503 DBBind(hStmt, 13, DB_SQLTYPE_INTEGER, (UINT32)m_lastPasswordChange);
504 DBBind(hStmt, 14, DB_SQLTYPE_INTEGER, m_minPasswordLength);
505 DBBind(hStmt, 15, DB_SQLTYPE_INTEGER, (UINT32)m_disabledUntil);
506 DBBind(hStmt, 16, DB_SQLTYPE_INTEGER, (UINT32)m_lastLogin);
507 DBBind(hStmt, 17, DB_SQLTYPE_VARCHAR, m_xmppId, DB_BIND_STATIC);
508 DBBind(hStmt, 18, DB_SQLTYPE_TEXT, m_userDn, DB_BIND_STATIC);
509 DBBind(hStmt, 19, DB_SQLTYPE_INTEGER, m_id);
510
511 bool success = DBBegin(hdb);
512 if (success)
513 {
514 success = DBExecute(hStmt);
515 if (success)
516 {
517 success = saveCustomAttributes(hdb);
518 }
519 if (success)
520 DBCommit(hdb);
521 else
522 DBRollback(hdb);
523 }
524 DBFreeStatement(hStmt);
525 return success;
526 }
527
528 /**
529 * Delete object from database
530 */
531 bool User::deleteFromDatabase(DB_HANDLE hdb)
532 {
533 TCHAR query[256];
534 bool success;
535
536 success = DBBegin(hdb);
537 if (success)
538 {
539 _sntprintf(query, 256, _T("DELETE FROM users WHERE id=%d"), m_id);
540 success = DBQuery(hdb, query);
541 if (success)
542 {
543 _sntprintf(query, 256, _T("DELETE FROM user_profiles WHERE user_id=%d"), m_id);
544 success = DBQuery(hdb, query);
545 if (success)
546 {
547 _sntprintf(query, 256, _T("DELETE FROM userdb_custom_attributes WHERE object_id=%d"), m_id);
548 success = DBQuery(hdb, query);
549 }
550 }
551 if (success)
552 DBCommit(hdb);
553 else
554 DBRollback(hdb);
555 }
556 return success;
557 }
558
559 /**
560 * Validate user's password
561 */
562 bool User::validatePassword(const TCHAR *password)
563 {
564 PasswordHash ph;
565 CalculatePasswordHash(password, m_password.hashType, &ph, m_password.salt);
566 bool success = !memcmp(ph.hash, m_password.hash, PWD_HASH_SIZE(m_password.hashType));
567 if (success && m_password.hashType == PWD_HASH_SHA1)
568 {
569 // regenerate password hash if old format is used
570 CalculatePasswordHash(password, PWD_HASH_SHA256, &m_password);
571 m_flags |= UF_MODIFIED;
572 }
573 return success;
574 }
575
576 /**
577 * Set user's password
578 * For non-UNICODE build, password must be UTF-8 encoded
579 */
580 void User::setPassword(const TCHAR *password, bool clearChangePasswdFlag)
581 {
582 CalculatePasswordHash(password, PWD_HASH_SHA256, &m_password);
583 m_graceLogins = MAX_GRACE_LOGINS;
584 m_flags |= UF_MODIFIED;
585 if (clearChangePasswdFlag)
586 m_flags &= ~UF_CHANGE_PASSWORD;
587 SendUserDBUpdate(USER_DB_MODIFY, m_id, this);
588 }
589
590 /**
591 * Fill NXCP message with user data
592 */
593 void User::fillMessage(NXCPMessage *msg)
594 {
595 UserDatabaseObject::fillMessage(msg);
596
597 msg->setField(VID_USER_FULL_NAME, m_fullName);
598 msg->setField(VID_AUTH_METHOD, (WORD)m_authMethod);
599 msg->setField(VID_CERT_MAPPING_METHOD, (WORD)m_certMappingMethod);
600 msg->setField(VID_CERT_MAPPING_DATA, CHECK_NULL_EX(m_certMappingData));
601 msg->setField(VID_LAST_LOGIN, (UINT32)m_lastLogin);
602 msg->setField(VID_LAST_PASSWORD_CHANGE, (UINT32)m_lastPasswordChange);
603 msg->setField(VID_MIN_PASSWORD_LENGTH, (WORD)m_minPasswordLength);
604 msg->setField(VID_DISABLED_UNTIL, (UINT32)m_disabledUntil);
605 msg->setField(VID_AUTH_FAILURES, (UINT32)m_authFailures);
606 msg->setField(VID_XMPP_ID, m_xmppId);
607
608 FillGroupMembershipInfo(msg, m_id);
609 }
610
611 /**
612 * Modify user object from NXCP message
613 */
614 void User::modifyFromMessage(NXCPMessage *msg)
615 {
616 UserDatabaseObject::modifyFromMessage(msg);
617
618 UINT32 fields = msg->getFieldAsUInt32(VID_FIELDS);
619
620 if (fields & USER_MODIFY_FULL_NAME)
621 msg->getFieldAsString(VID_USER_FULL_NAME, m_fullName, MAX_USER_FULLNAME);
622 if (fields & USER_MODIFY_AUTH_METHOD)
623 m_authMethod = msg->getFieldAsUInt16(VID_AUTH_METHOD);
624 if (fields & USER_MODIFY_PASSWD_LENGTH)
625 m_minPasswordLength = msg->getFieldAsUInt16(VID_MIN_PASSWORD_LENGTH);
626 if (fields & USER_MODIFY_TEMP_DISABLE)
627 m_disabledUntil = (time_t)msg->getFieldAsUInt32(VID_DISABLED_UNTIL);
628 if (fields & USER_MODIFY_CERT_MAPPING)
629 {
630 m_certMappingMethod = msg->getFieldAsUInt16(VID_CERT_MAPPING_METHOD);
631 safe_free(m_certMappingData);
632 m_certMappingData = msg->getFieldAsString(VID_CERT_MAPPING_DATA);
633 }
634 if (fields & USER_MODIFY_XMPP_ID)
635 msg->getFieldAsString(VID_XMPP_ID, m_xmppId, MAX_XMPP_ID_LEN);
636 if (fields & USER_MODIFY_GROUP_MEMBERSHIP)
637 {
638 int count = (int)msg->getFieldAsUInt32(VID_NUM_GROUPS);
639 UINT32 *groups = NULL;
640 if (count > 0)
641 {
642 groups = (UINT32 *)malloc(sizeof(UINT32) * count);
643 msg->getFieldAsInt32Array(VID_GROUPS, (UINT32)count, groups);
644 }
645 UpdateGroupMembership(m_id, count, groups);
646 safe_free(groups);
647 }
648
649 // Clear intruder lockout flag if user is not disabled anymore
650 if (!(m_flags & UF_DISABLED))
651 m_flags &= ~UF_INTRUDER_LOCKOUT;
652 }
653
654 /**
655 * Increase auth failures and lockout account if threshold reached
656 */
657 void User::increaseAuthFailures()
658 {
659 m_authFailures++;
660
661 int lockoutThreshold = ConfigReadInt(_T("IntruderLockoutThreshold"), 0);
662 if ((lockoutThreshold > 0) && (m_authFailures >= lockoutThreshold))
663 {
664 m_disabledUntil = time(NULL) + ConfigReadInt(_T("IntruderLockoutTime"), 30) * 60;
665 m_flags |= UF_DISABLED | UF_INTRUDER_LOCKOUT;
666 }
667
668 m_flags |= UF_MODIFIED;
669 SendUserDBUpdate(USER_DB_MODIFY, m_id, this);
670 }
671
672 /**
673 * Enable user account
674 */
675 void User::enable()
676 {
677 m_authFailures = 0;
678 m_graceLogins = MAX_GRACE_LOGINS;
679 m_disabledUntil = 0;
680 m_flags &= ~(UF_DISABLED | UF_INTRUDER_LOCKOUT);
681 m_flags |= UF_MODIFIED;
682 SendUserDBUpdate(USER_DB_MODIFY, m_id, this);
683 }
684
685 void User::setFullName(const TCHAR *fullName)
686 {
687 if (_tcscmp(CHECK_NULL_EX(m_fullName), CHECK_NULL_EX(fullName)))
688 {
689 nx_strncpy(m_fullName, CHECK_NULL_EX(fullName), MAX_USER_FULLNAME);
690 m_flags |= UF_MODIFIED;
691 }
692 }
693
694 /*****************************************************************************
695 ** Group
696 ****************************************************************************/
697
698 /**
699 * Constructor for group object - create from database
700 * Expects fields in the following order:
701 * id,name,system_access,flags,description,guid,ldap_dn
702 */
703 Group::Group(DB_RESULT hr, int row) : UserDatabaseObject(hr, row)
704 {
705 DB_RESULT hResult;
706 TCHAR query[256];
707
708 _sntprintf(query, 256, _T("SELECT user_id FROM user_group_members WHERE group_id=%d"), m_id);
709 hResult = DBSelect(g_hCoreDB, query);
710 if (hResult != NULL)
711 {
712 m_memberCount = DBGetNumRows(hResult);
713 if (m_memberCount > 0)
714 {
715 m_members = (UINT32 *)malloc(sizeof(UINT32) * m_memberCount);
716 for(int i = 0; i < m_memberCount; i++)
717 m_members[i] = DBGetFieldULong(hResult, i, 0);
718 }
719 else
720 {
721 m_members = NULL;
722 }
723 DBFreeResult(hResult);
724 }
725
726 loadCustomAttributes(g_hCoreDB);
727 }
728
729 /**
730 * Constructor for group object - create "Everyone" group
731 */
732 Group::Group() : UserDatabaseObject()
733 {
734 m_id = GROUP_EVERYONE;
735 _tcscpy(m_name, _T("Everyone"));
736 m_flags = UF_MODIFIED;
737 m_systemRights = 0;
738 _tcscpy(m_description, _T("Built-in everyone group"));
739 uuid_generate(m_guid);
740 m_memberCount = 0;
741 m_members = NULL;
742 }
743
744 /**
745 * Constructor for group object - create new group
746 */
747 Group::Group(UINT32 id, const TCHAR *name) : UserDatabaseObject(id, name)
748 {
749 m_memberCount = 0;
750 m_members = NULL;
751 }
752
753 /**
754 * Destructor for group object
755 */
756 Group::~Group()
757 {
758 safe_free(m_members);
759 }
760
761 /**
762 * Save object to database
763 */
764 bool Group::saveToDatabase(DB_HANDLE hdb)
765 {
766 TCHAR guidText[64];
767
768 // Clear modification flag
769 m_flags &= ~UF_MODIFIED;
770
771 DB_STATEMENT hStmt;
772 if (IsDatabaseRecordExist(hdb, _T("user_groups"), _T("id"), m_id))
773 {
774 hStmt = DBPrepare(hdb, _T("UPDATE user_groups SET name=?,system_access=?,flags=?,description=?,guid=?,ldap_dn=? WHERE id=?"));
775 }
776 else
777 {
778 hStmt = DBPrepare(hdb, _T("INSERT INTO user_groups (name,system_access,flags,description,guid,ldap_dn,id) VALUES (?,?,?,?,?,?,?)"));
779 }
780 if (hStmt == NULL)
781 return false;
782
783 DBBind(hStmt, 1, DB_SQLTYPE_VARCHAR, m_name, DB_BIND_STATIC);
784 DBBind(hStmt, 2, DB_SQLTYPE_BIGINT, m_systemRights);
785 DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, m_flags);
786 DBBind(hStmt, 4, DB_SQLTYPE_VARCHAR, m_description, DB_BIND_STATIC);
787 DBBind(hStmt, 5, DB_SQLTYPE_VARCHAR, uuid_to_string(m_guid, guidText), DB_BIND_STATIC);
788 DBBind(hStmt, 6, DB_SQLTYPE_TEXT, m_userDn, DB_BIND_STATIC);
789 DBBind(hStmt, 7, DB_SQLTYPE_INTEGER, m_id);
790
791 bool success = DBBegin(hdb);
792 if (success)
793 {
794 success = DBExecute(hStmt);
795 if (success)
796 {
797 DBFreeStatement(hStmt);
798 hStmt = DBPrepare(hdb, _T("DELETE FROM user_group_members WHERE group_id=?"));
799 if (hStmt != NULL)
800 {
801 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
802 success = DBExecute(hStmt);
803 }
804 else
805 {
806 success = false;
807 }
808
809 if (success && (m_memberCount > 0))
810 {
811 DBFreeStatement(hStmt);
812 hStmt = DBPrepare(hdb, _T("INSERT INTO user_group_members (group_id,user_id) VALUES (?,?)"));
813 if (hStmt != NULL)
814 {
815 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
816 for(int i = 0; (i < m_memberCount) && success; i++)
817 {
818 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, m_members[i]);
819 success = DBExecute(hStmt);
820 }
821 }
822 else
823 {
824 success = false;
825 }
826 }
827
828 if (success)
829 {
830 success = saveCustomAttributes(hdb);
831 }
832 }
833 if (success)
834 DBCommit(hdb);
835 else
836 DBRollback(hdb);
837 }
838
839 if (hStmt != NULL)
840 DBFreeStatement(hStmt);
841
842 return success;
843 }
844
845 /**
846 * Delete object from database
847 */
848 bool Group::deleteFromDatabase(DB_HANDLE hdb)
849 {
850 TCHAR query[256];
851 bool success;
852
853 success = DBBegin(hdb);
854 if (success)
855 {
856 _sntprintf(query, 256, _T("DELETE FROM user_groups WHERE id=%d"), m_id);
857 success = DBQuery(hdb, query);
858 if (success)
859 {
860 _sntprintf(query, 256, _T("DELETE FROM user_group_members WHERE group_id=%d"), m_id);
861 success = DBQuery(hdb, query);
862 if (success)
863 {
864 _sntprintf(query, 256, _T("DELETE FROM userdb_custom_attributes WHERE object_id=%d"), m_id);
865 success = DBQuery(hdb, query);
866 }
867 }
868 if (success)
869 DBCommit(hdb);
870 else
871 DBRollback(hdb);
872 }
873 return success;
874 }
875
876 /**
877 * Check if given user is a member
878 */
879 bool Group::isMember(UINT32 userId)
880 {
881 int i;
882
883 if (m_id == GROUP_EVERYONE)
884 return true;
885
886 //This is done not to assign disabled group rights on a enabled user
887 if ((m_flags & UF_SYNC_EXCEPTION) || (m_flags & UF_DISABLED))
888 return false;
889
890 for(i = 0; i < m_memberCount; i++)
891 if (m_members[i] == userId)
892 return true;
893 return false;
894 }
895
896 /**
897 * Add user to group
898 */
899 void Group::addUser(UINT32 userId)
900 {
901 int i;
902
903 // Check if user already in group
904 for(i = 0; i < m_memberCount; i++)
905 if (m_members[i] == userId)
906 return;
907
908 // Not in group, add it
909 m_memberCount++;
910 m_members = (UINT32 *)realloc(m_members, sizeof(UINT32) * m_memberCount);
911 m_members[i] = userId;
912
913 m_flags |= UF_MODIFIED;
914
915 SendUserDBUpdate(USER_DB_MODIFY, m_id, this);
916 }
917
918 /**
919 * Delete user from group
920 */
921 void Group::deleteUser(UINT32 userId)
922 {
923 int i;
924
925 for(i = 0; i < m_memberCount; i++)
926 if (m_members[i] == userId)
927 {
928 m_memberCount--;
929 memmove(&m_members[i], &m_members[i + 1], sizeof(UINT32) * (m_memberCount - i));
930 m_flags |= UF_MODIFIED;
931 SendUserDBUpdate(USER_DB_MODIFY, m_id, this);
932 break;
933 }
934 }
935
936 /**
937 * Get group members.
938 */
939 int Group::getMembers(UINT32 **members)
940 {
941 *members = m_members;
942 return m_memberCount;
943 }
944
945 /**
946 * Fill NXCP message with user group data
947 */
948 void Group::fillMessage(NXCPMessage *msg)
949 {
950 UINT32 varId;
951 int i;
952
953 UserDatabaseObject::fillMessage(msg);
954
955 msg->setField(VID_NUM_MEMBERS, (UINT32)m_memberCount);
956 for(i = 0, varId = VID_GROUP_MEMBER_BASE; i < m_memberCount; i++, varId++)
957 msg->setField(varId, m_members[i]);
958 }
959
960 /**
961 * Modify group object from NXCP message
962 */
963 void Group::modifyFromMessage(NXCPMessage *msg)
964 {
965 int i;
966 UINT32 varId, fields;
967
968 UserDatabaseObject::modifyFromMessage(msg);
969
970 fields = msg->getFieldAsUInt32(VID_FIELDS);
971 if (fields & USER_MODIFY_MEMBERS)
972 {
973 UINT32 *members = m_members;
974 int count = m_memberCount;
975 m_memberCount = msg->getFieldAsUInt32(VID_NUM_MEMBERS);
976 if (m_memberCount > 0)
977 {
978 m_members = (UINT32 *)malloc(sizeof(UINT32) * m_memberCount);
979 for(i = 0, varId = VID_GROUP_MEMBER_BASE; i < m_memberCount; i++, varId++)
980 {
981 m_members[i] = msg->getFieldAsUInt32(varId);
982
983 // check if new member
984 bool found = false;
985 for(int j = 0; j < count; j++)
986 {
987 if (members[j] == m_members[i])
988 {
989 members[j] = 0xFFFFFFFF; // mark as found
990 found = true;
991 break;
992 }
993 }
994
995 if (!found)
996 SendUserDBUpdate(USER_DB_MODIFY, m_members[i]); // new member added
997 }
998 for(i = 0; i < count; i++)
999 if (members[i] != 0xFFFFFFFF) // not present in new list
1000 SendUserDBUpdate(USER_DB_MODIFY, members[i]);
1001 }
1002 else
1003 {
1004 m_members = NULL;
1005
1006 // notify change for all old members
1007 for(i = 0; i < count; i++)
1008 SendUserDBUpdate(USER_DB_MODIFY, members[i]);
1009 }
1010 safe_free(members);
1011 }
1012 }