1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
device.php
См. документацию.
1<?php
2
9
10namespace Bitrix\Main\Authentication;
11
12use Bitrix\Main;
13use Bitrix\Main\Web;
14use Bitrix\Main\Security;
15use Bitrix\Main\Config\Option;
16use Bitrix\Main\Authentication\Internal\EO_UserDevice;
17use Bitrix\Main\Authentication\Internal\EO_UserDeviceLogin;
18use Bitrix\Main\Web\UserAgent\Browser;
19use Bitrix\Main\Web\UserAgent\DeviceType;
20use Bitrix\Main\Service\GeoIp;
21use Bitrix\Main\Service\GeoIp\Internal\GeonameTable;
22use Bitrix\Main\Mail;
23use Bitrix\Main\Localization\LanguageTable;
24use Bitrix\Main\Localization\Loc;
25use Bitrix\ImBot\Bot\Marta;
26use Bitrix\Im\V2\Message;
27use Bitrix\Im\V2\Entity\User\User;
28
29class Device
30{
31 protected const COOKIE_NAME = 'UIDD';
32 public const EMAIL_EVENT = 'NEW_DEVICE_LOGIN';
33
34 public static function addLogin(Context $context, array $user): void
35 {
36 $device = static::findByCookie($context);
37
38 if ($device === null)
39 {
40 $cookable = false;
41 $device = static::findByUserAgent($context);
42
43 if ($device === null && $context->getApplicationPasswordId())
44 {
45 $device = static::findByAppPasswordId($context);
46 }
47 }
48 else
49 {
50 // found by a cookie = supports cookies
51 $cookable = true;
52 }
53
54 if ($device === null)
55 {
56 // add a new device for the user
57 $device = static::add($context);
58 $deviceLogin = static::addDeviceLogin($device, $context);
59
60 $notifyByEmail = (Option::get('main', 'user_device_notify', 'N') === 'Y');
61 $notifyByIm = (Option::get('main', 'user_device_notify_im', 'N') === 'Y' && Main\ModuleManager::isModuleInstalled('im') && Main\ModuleManager::isModuleInstalled('imbot'));
62
63 if ($notifyByEmail || $notifyByIm)
64 {
65 // send notification to the user
66 static::notifyUser($device, $deviceLogin, $user);
67 }
68 }
69 else
70 {
71 // update to actual data
72 static::update($device, $context, $cookable);
73 static::addDeviceLogin($device, $context);
74 }
75
76 static::setCookie($device->getDeviceUid());
77 }
78
79 protected static function findByCookie(Context $context): ?EO_UserDevice
80 {
81 $request = Main\Context::getCurrent()->getRequest();
82 $deviceUid = $request->getCookie(static::COOKIE_NAME);
83
84 if (is_string($deviceUid) && $deviceUid != '')
85 {
87 ->setSelect(['*'])
88 ->where('USER_ID', $context->getUserId())
89 ->where('DEVICE_UID', $deviceUid)
90 ->fetchObject()
91 ;
92 }
93
94 return null;
95 }
96
97 protected static function findByUserAgent(Context $context): ?EO_UserDevice
98 {
99 $request = Main\Context::getCurrent()->getRequest();
100 $userAgent = $request->getUserAgent();
101
102 // only user agents not supporting cookies
104 ->setSelect(['*'])
105 ->where('USER_ID', $context->getUserId())
106 ->where('COOKABLE', false)
107 ->exec()
108 ;
109
110 while ($device = $query->fetchObject())
111 {
112 if (static::match($userAgent, $device->getUserAgent()))
113 {
114 return $device;
115 }
116 }
117
118 return null;
119 }
120
122 {
123 $request = Main\Context::getCurrent()->getRequest();
124 $userAgent = $request->getUserAgent();
125
126 // only user agents with the same APP_PASSWORD_ID
128 ->setSelect(['*'])
129 ->where('USER_ID', $context->getUserId())
130 ->where('COOKABLE', true)
131 ->where('APP_PASSWORD_ID', $context->getApplicationPasswordId())
132 ->exec()
133 ;
134
135 while ($device = $query->fetchObject())
136 {
137 if (static::match($userAgent, $device->getUserAgent()))
138 {
139 return $device;
140 }
141 }
142
143 return null;
144 }
145
146 protected static function match(?string $userAgent, ?string $patternAgent): bool
147 {
148 $pattern = preg_replace('/\\d+/i', '\\d+', preg_quote((string)$patternAgent, '/'));
149
150 return preg_match("/{$pattern}/i", (string)$userAgent);
151 }
152
153 protected static function add(Context $context): EO_UserDevice
154 {
155 $browser = Browser::detect();
157
158 $device
159 ->setUserId($context->getUserId())
160 ->setDeviceUid(Security\Random::getString(32))
161 ->setDeviceType($browser->getDeviceType())
162 ->setBrowser($browser->getName())
163 ->setPlatform($browser->getPlatform())
164 ->setUserAgent($browser->getUserAgent())
165 ->setAppPasswordId($context->getApplicationPasswordId())
166 ->save()
167 ;
168
169 return $device;
170 }
171
172 protected static function update(EO_UserDevice $device, Context $context, bool $cookable): void
173 {
174 $browser = Browser::detect();
175
176 $device
177 ->setDeviceType($browser->getDeviceType())
178 ->setBrowser($browser->getName())
179 ->setPlatform($browser->getPlatform())
180 ->setUserAgent($browser->getUserAgent())
181 ->setCookable($cookable)
182 ->setAppPasswordId($context->getApplicationPasswordId())
183 ->save()
184 ;
185 }
186
187 protected static function setCookie(string $value): void
188 {
189 $cookie = new Web\Cookie(static::COOKIE_NAME, $value, time() + 60 * 60 * 24 * 30 * 12);
190 Main\Context::getCurrent()->getResponse()->addCookie($cookie);
191 }
192
194 {
196
198
199 $login
200 ->setDeviceId($device->getId())
201 ->setLoginDate(new Main\Type\DateTime())
202 ->setIp($ip)
203 ->setAppPasswordId($context->getApplicationPasswordId())
204 ->setStoredAuthId($context->getStoredAuthId())
205 ->setHitAuthId($context->getHitAuthId())
206 ;
207
208 if (Option::get('main', 'user_device_geodata', 'N') === 'Y')
209 {
210 $ipData = GeoIp\Manager::getDataResult($ip, '', ['cityGeonameId']);
211
212 if ($ipData && $ipData->isSuccess())
213 {
214 $data = $ipData->getGeoData();
215
216 $login
217 ->setCityGeoid($data->cityGeonameId)
218 ->setRegionGeoid($data->subRegionGeonameId ?? $data->regionGeonameId)
219 ->setCountryIsoCode($data->countryCode)
220 ;
221 }
222 }
223
224 $login->save();
225
226 return $login;
227 }
228
229 protected static function notifyUser(EO_UserDevice $device, EO_UserDeviceLogin $deviceLogin, array $user): void
230 {
231 // we have settings in the main module options
232 $codes = unserialize(Option::get('main', 'user_device_notify_codes'), ['allowed_classes' => false]);
233 if (!empty($codes))
234 {
235 $userCodes = \CAccess::GetUserCodesArray($user['ID']);
236 if (empty(array_intersect($codes, $userCodes)))
237 {
238 // no need to notify
239 return;
240 }
241 }
242
243 $currentLang = Main\Context::getCurrent()->getLanguage();
244 $lang = $user['LANGUAGE_ID'] != '' ? $user['LANGUAGE_ID'] : $currentLang;
245
246 // Devices
247 $deviceTypes = DeviceType::getDescription($lang);
248
249 // geoIP
250 $geonames = [];
251 if (Option::get('main', 'user_device_geodata', 'N') === 'Y')
252 {
253 $geonames = static::getGeoNames($deviceLogin, $lang);
254 }
255
256 $fields = [
257 'USER_ID' => $user['ID'],
258 'EMAIL' => $user['EMAIL'],
259 'LOGIN' => $user['LOGIN'],
260 'NAME' => $user['NAME'],
261 'LAST_NAME' => $user['LAST_NAME'],
262 'DEVICE' => $deviceTypes[$device->getDeviceType()],
263 'BROWSER' => $device->getBrowser(),
264 'PLATFORM' => $device->getPlatform(),
265 'USER_AGENT' => $device->getUserAgent(),
266 'IP' => $deviceLogin->getIp(),
267 'DATE' => $deviceLogin->getLoginDate(),
268 'COUNTRY' => $geonames['COUNTRY'] ?? '',
269 'REGION' => $geonames['REGION'] ?? '',
270 'CITY' => $geonames['CITY'] ?? '',
271 'LOCATION' => $geonames['LOCATION'] ?? '',
272 ];
273
274 if (Option::get('main', 'user_device_notify', 'N') === 'Y')
275 {
276 // email
277 $site = $user['LID'];
278 if (!$site)
279 {
280 $site = Main\Context::getCurrent()->getSite();
281 if (!$site)
282 {
283 $site = \CSite::GetDefSite();
284 }
285 }
286
288 'EVENT_NAME' => self::EMAIL_EVENT,
289 'C_FIELDS' => $fields,
290 'LID' => $site,
291 'LANGUAGE_ID' => $lang,
292 ]);
293 }
294
295 if (Option::get('main', 'user_device_notify_im', 'N') === 'Y' && Main\Loader::includeModule('im') && Main\Loader::includeModule('imbot'))
296 {
297 // chat notification
298 $replace = [];
299 foreach ($fields as $key => $value)
300 {
301 $replace["#$key#"] = $value;
302 }
303 $message = Loc::getMessage('main_device_message', $replace, $lang);
304 $pushMessage = Loc::getMessage('main_device_push_message', null, $lang);
305
306 $infoBotId = Marta::getBotId();
307 if ($infoBotId)
308 {
309 $chat = User::getInstance($user['ID'])->getChatWith($infoBotId);
310 if ($chat)
311 {
312 $chatMessage = new Message();
313 $chatMessage
314 ->setMessage($message)
315 ->setPushMessage($pushMessage)
316 ->setAuthorId($infoBotId)
317 ;
318 $chat->sendMessage($chatMessage);
319 }
320 }
321 }
322 }
323
324 protected static function getGeoNames(EO_UserDeviceLogin $deviceLogin, string $lang): array
325 {
326 // City and Region names
327 $geoids = array_filter([$deviceLogin->getCityGeoid(), $deviceLogin->getRegionGeoid()]);
328 $geonames = GeonameTable::get($geoids);
329
330 $city = '';
331 $region = '';
332 if (!empty($geonames))
333 {
334 $currentLang = Main\Context::getCurrent()->getLanguage();
335
336 $langCode = '';
337 if ($lang != $currentLang)
338 {
339 $language = LanguageTable::getList([
340 'filter' => ['=LID' => $lang, '=ACTIVE' => 'Y'],
341 'cache' => ['ttl' => 86400],
342 ])->fetchObject();
343
344 if ($language)
345 {
346 $langCode = $language->getCode();
347 }
348 }
349 if ($langCode == '')
350 {
351 $langCode = Main\Context::getCurrent()->getLanguageObject()->getCode();
352 }
353
354 if (($cityCode = $deviceLogin->getCityGeoid()) > 0)
355 {
356 $city = $geonames[$cityCode][$langCode] ?? $geonames[$cityCode]['en'] ?? '';
357 }
358 if (($regionCode = $deviceLogin->getRegionGeoid()) > 0)
359 {
360 $region = $geonames[$regionCode][$langCode] ?? $geonames[$regionCode]['en'] ?? '';
361 }
362 }
363
364 // Country name
365 $country = '';
366 if (($countryCode = $deviceLogin->getCountryIsoCode()) != '')
367 {
368 $countries = \GetCountries($lang);
369 $country = $countries[$countryCode]['NAME'];
370 }
371
372 // Combined location
373 $location = implode(', ', array_filter([$city, $region, $country]));
374
375 return [
376 'COUNTRY' => $country,
377 'REGION' => $region,
378 'CITY' => $city,
379 'LOCATION' => $location,
380 ];
381 }
382
388 public static function deleteDuplicatesAgent(int $lastId = 0)
389 {
391
392 $users = $connection->query("
393 select USER_ID
394 from b_user_device
395 where USER_ID > {$lastId}
396 group by USER_ID
397 order by USER_ID
398 limit 100
399 ");
400
401 $userId = null;
402 while ($user = $users->fetch())
403 {
404 $userId = $user['USER_ID'];
405
406 $devices = $connection->query("
407 select *
408 from b_user_device
409 where USER_ID = {$userId}
410 and COOKABLE = 'Y'
411 and APP_PASSWORD_ID is not null
412 order by ID
413 ")->fetchAll();
414
415 $deleted = [];
416 for ($i = 0, $count = count($devices); $i < $count; $i++)
417 {
418 $device = $devices[$i];
419
420 if (isset($deleted[$device['ID']]))
421 {
422 continue;
423 }
424
425 for ($j = $i + 1; $j < $count; $j++)
426 {
427 $deviceDouble = $devices[$j];
428
429 if ($deviceDouble['APP_PASSWORD_ID'] == $device['APP_PASSWORD_ID'] && static::match($deviceDouble['USER_AGENT'], $device['USER_AGENT']))
430 {
431 $connection->query("
432 update b_user_device_login
433 set DEVICE_ID = {$device['ID']}
434 where DEVICE_ID = {$deviceDouble['ID']}
435 ");
436
437 $connection->query("
438 delete from b_user_device
439 where ID = {$deviceDouble['ID']}
440 ");
441
442 $deleted[$deviceDouble['ID']] = 1;
443 }
444 }
445 }
446 }
447
448 if ($userId !== null)
449 {
450 return "\\Bitrix\\Main\\Authentication\\Device::deleteDuplicatesAgent({$userId});";
451 }
452
453 return '';
454 }
455}
$connection
Определения actionsdefinitions.php:38
$count
Определения admin_tab.php:4
if(!Loader::includeModule('catalog')) if(!AccessController::getCurrent() ->check(ActionDictionary::ACTION_PRICE_EDIT)) if(!check_bitrix_sessid()) $request
Определения catalog_reindex.php:36
$login
Определения change_password.php:8
if(!is_object($USER)||! $USER->IsAuthorized()) $userId
Определения check_mail.php:18
static getConnection($name="")
Определения application.php:638
const EMAIL_EVENT
Определения device.php:32
static update(EO_UserDevice $device, Context $context, bool $cookable)
Определения device.php:172
static findByAppPasswordId(Context $context)
Определения device.php:121
static getGeoNames(EO_UserDeviceLogin $deviceLogin, string $lang)
Определения device.php:324
const COOKIE_NAME
Определения device.php:31
static match(?string $userAgent, ?string $patternAgent)
Определения device.php:146
static addDeviceLogin(EO_UserDevice $device, Context $context)
Определения device.php:193
static notifyUser(EO_UserDevice $device, EO_UserDeviceLogin $deviceLogin, array $user)
Определения device.php:229
static findByCookie(Context $context)
Определения device.php:79
static findByUserAgent(Context $context)
Определения device.php:97
static addLogin(Context $context, array $user)
Определения device.php:34
static setCookie(string $value)
Определения device.php:187
static add(Context $context)
Определения device.php:153
static deleteDuplicatesAgent(int $lastId=0)
Определения device.php:388
static includeModule($moduleName)
Определения loader.php:67
static send(array $data)
Определения event.php:48
static isModuleInstalled($moduleName)
Определения modulemanager.php:125
static createObject($setDefaultValues=true)
Определения datamanager.php:232
static getDataResult($ip='', $lang='', array $required=[])
Определения manager.php:188
static getRealIp()
Определения manager.php:532
static detect(?string $userAgent=null)
Определения browser.php:113
static getDescription($lang=null)
Определения devicetype.php:22
if(!\Bitrix\Main\Loader::includeModule('clouds')) $lastId
Определения sync.php:68
$data['IS_AVAILABLE']
Определения .description.php:13
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$query
Определения get_search.php:11
$region
Определения .description.php:13
$context
Определения csv_new_setup.php:223
if(!defined('SITE_ID')) $lang
Определения include.php:91
Определения Uuid.php:3
Определения culture.php:9
Определения collection.php:2
$user
Определения mysql_to_pgsql.php:33
$country
Определения payment.php:59
$message
Определения payment.php:8
if(empty($signedUserToken)) $key
Определения quickway.php:257
$i
Определения factura.php:643
</p ></td >< td valign=top style='border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 2.0pt 0cm 2.0pt;height:9.0pt'>< p class=Normal align=center style='margin:0cm;margin-bottom:.0001pt;text-align:center;line-height:normal'>< a name=ТекстовоеПоле54 ></a ><?=($taxRate > count( $arTaxList) > 0) ? $taxRate."%"
Определения waybill.php:936
$location
Определения options.php:2729
if(!Loader::includeModule('sale')) $pattern
Определения index.php:20
$site
Определения yandex_run.php:614
$fields
Определения yandex_run.php:501