Проверка безопасности пользователей 1С

Со временем, даже в небольших конфигурациях 1С количество пользователей чаще всего увеличивается. А если еще и компания достаточно большая, то возникает необходимость следить за тем, чтобы учётки вовремя отключались. А еще и были безопасны с точки зрения использования. Например: достаточно часто пользователь меняет пароль, включена ли у него доменная авторизация и т.п., Фактически полноценная проверка безопасности пользователей 1С

В моём случае возникла необходимость регулярно мониторить служебные учётные данные. Количество разработчиков большое. Количество http и web сервисов не большое, но постоянно растёт, т.к. мы придерживаюсь принципа: один сервис — одна учётка для доступа. Делается это для того чтобы было удобнее логировать и отслеживать кто что и как. Соответственно необходимо чтобы эти служебные пользователи имели строго определенные доступы, и не имели возможность непосредственного входа в 1С через толстый и тонкий клиенты.

После калькуляции требований безопасности я выявил следующие показатели для того чтобы считать учётную запись «неблагонадёжной»:

  • нет доменной авторизации
  • разрешен вход в 1с (галочка «Доступ к информационной базе разрешен» в справочнике Пользователи)
  • у пользователя включены роли ЗапускВебКлиента, ЗапускТолстогоКлиента, ЗапускВнешнегоСоединения
  • При ответе «Да» на все три условия, пользователя помещаю в соответствующий список.
  • Кроме того дополнительно провожу проверку, что в правах одной из ролей есть права: ЗапускВебКлиента, ЗапускТолстогоКлиента, ЗапускВнешнегоСоединения

В итоге родил такую обработку:

Процедура СК_ГР_ПроверкаБезопасностиПользователя() экспорт
	УстановитьПривилегированныйРежим(Истина);
	Выборка = ПользователиИнформационнойБазы.ПолучитьПользователей(); 
	МассивДляОтчета=Новый Массив();
	данные="";   
	бд=НСтр(СтрокаСоединенияИнформационнойБазы(), "Ref"); 
	Для Каждого ЭлементМассива Из Выборка Цикл 
		если ЭлементМассива.АутентификацияОС=ложь тогда
			если ЭлементМассива.АутентификацияСтандартная=истина тогда				
				инф=Новый Структура("полноеимя,имя,email,датапароля,показыватьвспискевыбора,роли,пользователь,ЗапускВебКлиента,ЗапускТолстогоКлиента,ЗапускТонкогоКлиента,ЗапускВнешнегоСоединения");				
				инф.полноеимя=ЭлементМассива.ПолноеИмя;
				инф.имя=ЭлементМассива.Имя;
				инф.email=ЭлементМассива.АдресЭлектроннойПочты;
				инф.датапароля=ЭлементМассива.ДатаУстановкиПароля;
				инф.показыватьвспискевыбора=ЭлементМассива.ПоказыватьВСпискеВыбора;
				инф.роли="";
				
				ЕстьПароль = ЭлементМассива.ПарольУстановлен; 
				Роли = ЭлементМассива.Роли; 		
				Для Каждого Роль Из Роли Цикл 					
					если ПравоДоступа("ВебКлиент",Метаданные,Роль) тогда
						инф.роли=инф.роли+Роль.Имя+", ";
					иначеесли ПравоДоступа("ВнешнееСоединение",Метаданные,Роль) тогда
						инф.роли=инф.роли+Роль.Имя+", ";
					иначеесли ПравоДоступа("ТолстыйКлиент",Метаданные,Роль) тогда
						инф.роли=инф.роли+Роль.Имя+", ";
					иначеесли ПравоДоступа("ТонкийКлиент",Метаданные,Роль) тогда
						инф.роли=инф.роли+Роль.Имя+", ";
					конецесли;						
				КонецЦикла; 				
				поль=Справочники.Пользователи.НайтиПоРеквизиту("ИдентификаторПользователяИБ",ЭлементМассива.УникальныйИдентификатор);
				    ЗапускРоль="";
					если поль<>Справочники.Пользователи.ПустаяСсылка() тогда
						инф.ЗапускВебКлиента=УправлениеДоступом.ЕстьРоль("ЗапускВебКлиента",, поль);					
						если инф.ЗапускВебКлиента=истина тогда
							ЗапускРоль=ЗапускРоль+"Веб, ";	
						конецесли;
						инф.ЗапускТолстогоКлиента=УправлениеДоступом.ЕстьРоль("ЗапускТолстогоКлиента",, поль);
						если инф.ЗапускТолстогоКлиента=истина тогда
							ЗапускРоль=ЗапускРоль+"Толстый, ";	
						конецесли;						
						инф.ЗапускТонкогоКлиента=УправлениеДоступом.ЕстьРоль("ЗапускТонкогоКлиента",, поль);
						если инф.ЗапускТонкогоКлиента=истина тогда
							ЗапускРоль=ЗапускРоль+"Тонкий, ";	
						конецесли;						
						инф.ЗапускВнешнегоСоединения=УправлениеДоступом.ЕстьРоль("ЗапускВнешнегоСоединения",, поль);
						если инф.ЗапускВнешнегоСоединения=истина тогда
							ЗапускРоль=ЗапускРоль+"Внешние, ";	
						конецесли;
						
					конецесли;				
								
				инф.пользователь=поль.Наименование;				
				МассивДляОтчета.Добавить(инф);
				
				данные=данные+"<tr>";
				данные=данные+" <td>"+инф.полноеимя+"</td>";
				данные=данные+" <td>"+инф.имя+"</td>";
				данные=данные+" <td>"+инф.пользователь+"</td>";
				данные=данные+" <td>"+инф.email+"</td>";
				данные=данные+" <td>"+инф.датапароля+"</td>";
				данные=данные+" <td>"+инф.показыватьвспискевыбора+"</td>";				
				данные=данные+" <td>"+ЗапускРоль+"</td>";
				данные=данные+" <td>"+инф.роли+"</td>";
				данные=данные+"</tr>";
				
			Конецесли;
		Конецесли;
	КонецЦикла;
		
	если МассивДляОтчета.Количество()>0 тогда
		 			
		 	тело="<style>td {border: 1px solid;} th{border: 1px solid;} table.table {word-wrap:true;border-collapse: collapse;border-spacing: 0;}table.table > thead > tr > th {font-size: 14px;font-weight: normal;padding-top: 7px;padding-bottom: 7px;}.table > thead > tr > th {vertical-align: bottom;border-bottom: 2px solid #ddd;}table.table th {background-color: #153e76;}</style>";						
			тело=тело+"Критерии попадания в этот список: отключена доменная авторизация, разрешен вход в 1С<br/>";			
		 	тело=тело+"<strong>Подозрительные учётки</strong>: <br/>";
    		тело=тело+"<table class='table'>";
    		тело=тело+"  <thead>";
    		тело=тело+"    <tr>";
    		тело=тело+"     <th scope='col'> Полное имя</th>";
			тело=тело+"     <th scope='col'> Имя</th>";
			тело=тело+"     <th scope='col'> Пользователь</th>";			
			тело=тело+"     <th scope='col'> Email</th>";
			тело=тело+"     <th scope='col'> Дата пароля</th>";
			тело=тело+"     <th scope='col'> В списке выбора</th>";
			тело=тело+"     <th scope='col'> Вид запуска</th>";
			тело=тело+"     <th scope='col'> Роли с доступом в 1С</th>";
    		тело=тело+"   </tr>";
    		тело=тело+" </thead>";
    		тело=тело+" <tbody>";    		 
		 	тело=тело+данные;		 
		 	тело=тело+"  </tbody>";
    		тело=тело+"</table>";

						
		 emails=Новый Массив();	
		 emails.Добавить("цкуаука@куауцкацук.ru");
		 
		 для каждого email из emails цикл
			 если email<>"" тогда
				 попытка
	 			 		ПараметрыПисьма = Новый Структура("Кому, Тема, Тело,ТипТекста", email, "Подозрительные учётки в БД: "+бд, тело,"HTML");
						если бд="укму" тогда
						 УчетнаяЗаписьПочты = РаботаСПочтовымиСообщениями.ПолучитьСистемнуюУчетнуюЗапись();						
				 		 РаботаСПочтовымиСообщениями.ОтправитьСообщение(УчетнаяЗаписьПочты, ПараметрыПисьма);
					    иначеесли бд="куепуке" тогда
						 УчетнаяЗаписьПочты = РаботаСПочтовымиСообщениями.ПолучитьСистемнуюУчетнуюЗапись();
						 РаботаСПочтовымиСообщениями.ОтправитьСообщение(УчетнаяЗаписьПочты, ПараметрыПисьма);
					    иначеесли бд="укепуке" тогда					 
						 РаботаСПочтовымиСообщениями.ОтправитьПочтовоеСообщение(РаботаСПочтовымиСообщениями.СистемнаяУчетнаяЗапись(),ПараметрыПисьма);					 					 
					    иначеесли бд="куепук" тогда					 
						 РаботаСПочтовымиСообщениями.ОтправитьПочтовоеСообщение(РаботаСПочтовымиСообщениями.СистемнаяУчетнаяЗапись(),ПараметрыПисьма);					 
					    конецесли;						 
					исключение
						ЗаписьЖурналаРегистрации("Ошибка", УровеньЖурналаРегистрации.Ошибка,,ОписаниеОшибки(),);			
				 конецпопытки;
	 		 конецесли;									 
	     конеццикла;		 		 
	конецесли;
	  УстановитьПривилегированныйРежим(ложь);
КонецПроцедуры

Обработкой я перебираю всех пользователей информационной базы, и ищу соответствующего пользователя из справочника «Пользователи». В том случае, если пользователя признаю потенциально не безопасным, то отправляю заинтересованному лицу уведомление на электронную почту.

проверка безопасности пользователей 1С

Почитайте еще по теме 1С тут

Динамическая таблица значений на форме

Часто возникают ситуации, что необходимо отобразить данные на форме в виде таблицы (динамическая таблица значений на форме). Классический метод — добавить к документу (справочнику и т.п.) табличную часть — объект. Но из этого возникает значительный минус — при записи данные будут храниться в БД. А если это не нужно, и данные должны подтягиваться динамически из других таблиц? Выход есть — создать таблицу не как объект, а как переменную формы. В этом случае данные в БД сохраняться не будут. Что для этого нужно?

Создать реквизит с типом «Таблица значений»:

динамическая таблица значений на форме

Затем добавить колонки реквизита, указывая для них необходимый тип:

Далее, перетащите получившуюся таблицу на форму:

И остаётся только при событии формы «При создании на сервере», заполнить эту таблицу:

ТЗДвижения.Очистить();
 
 НомерИП=СокрЛП(объект.НомерИП);
 ТЗ=Новый ТаблицаЗначений();
 ТЗ.Колонки.Добавить("Дата");
 ТЗ.Колонки.Добавить("ИсточникДанных");
 ТЗ.Колонки.Добавить("ВидДанных");
 ТЗ.Колонки.Добавить("Комментарий");
 ТЗ.Колонки.Добавить("СсылкаНаДокумент");
...
заполняем таблицу
...
 ТЗ.Сортировать("Дата УБЫВ"); 
 ТЗДвижения.Загрузить(ТЗ);

Как результат — получим динамически формируемую таблицу на управляемой форме 1С (динамическая таблица значений). Другие статьи по теме 1С можете найти здесь

Кнопка выбора периода

Довольно редко использую, потому забываю постоянно, вот сделал себе очередную шпаргалку, как делается кнопка выбора периода в 1с на управляемых формах. Итак, первым делом создайте реквизит формы, с типом «стандартный период»:

кнопка выбора периода

Далее, перетаскиваем этот реквизит на форму, и добавляю обычные реквизиты с типом Дата+Время:

Осталось всего ничего — добавить обработчик события «при изменении»:


&НаКлиенте
Процедура ВыборПериодаПриИзменении(Элемент)
	НачПериода = ВыборПериода.ДатаНачала;
	КонПериода = ВыборПериода.ДатаОкончания;
	
	Сообщить("Начало: " + НачПериода + ", окончание: " + КонПериода);
КонецПроцедуры

На этом задача «кнопка выбора периода» будем считать завершена. Еще больше всяких шпаргалок по 1С, легко найдется здесь.

Шрифты при формировании pdf из табличного документа

В виду того, что организация плавно переходит на использование свободных от лицензий шрифтов (и соответственно не попадающих на санкции), то начал работу по переводу некоторых печатных форм в 1С на новые шрифты. Сегодня столкнулся с интересной проблемой, а именно: шрифты при формировании pdf из табличного документа выставляются не верно. Проблема актуальна только при формировании PDF на «сервере». На «клиенте» — всё корректно. Например если в поле табличного документа указать шрифт PT Astra Serif, то в документе pdf они станут ArialMT:

Шрифты при формировании pdf из табличного документа

Мало того, даже если при формировании документа прямо указать шрифт…то результат тот же:

Область = Макет.ПолучитьОбласть("Шапка");
Область.Область().Шрифт=Новый Шрифт("PT Astra Serif",11);   
....
ТабДок.Область().Шрифт=Новый Шрифт("PT Astra Serif",11); 
 Вложения=Новый КоллекцияВложенийPDF();
 ТабДок.Записать(врмф , ТипФайлаТабличногоДокумента.PDF_A_3,Вложения);

Да, тут я попробовал даже использовать относительно новый функционал по сохранению PDF, именно использование формата PDF_A_3, который позволяет добавлять «вложения». Но толкового описания и примеров в сети я не нашел, а документация от 1С очень скудна. Возможно при помощи «вложений» можно прикрепить этот шрифт для использования. Но не факт.

Открыв сформировавшийся файл pdf блокнотом, можно увидеть что платформа 1С даже не пытается упомянуть нужные шрифты:

Хотя если сохранять тот-же самый табличный документ сначала в формат docx, а затем средствами Word сохранить в pdf, то всё отлично сохраняется, и PDF получается корректный:

Ну что я могу сказать? На лицо похоже ошибка в платформе. Будем ждать обновлений, а пока решил проблему формированием файла в формате html5:

 врмф=ПолучитьИмяВременногоФайла(".html");  
 ТабДок.Записать(врмф , ТипФайлаТабличногоДокумента.HTML5);
 дд = Новый ДвоичныеДанные(врмф);

Что в принципе в моём случае, проблему «не верные шрифты при формировании pdf» временно закрыло

Сколько дней в году

Иногда возникает задача, как определить сколько дней в году на языке 1С. Их есть у нас (с), ну я имею ввиду решение.. Странно конечно, что в 1С нет готовой функции, приходится напрягать мозжечок (ну или гугл/яндекс) и велосипедить. Ну на самом деле есть несколько вариантов, Например первый вариант:

Функция ДнейВЭтомГоду()
 Дата = ТекущаяДата();
 ДнейВГоду = ДеньГода(КонецГода(Дата));
 возврат ДнейВГоду;
конецфункции

Основан на функциях 1С которые позволяют определить порядковый номер дня по переданной дате. Т.е. передаём в неё последний день текущего года, и получаем или 365 или 366, в зависимости от високосности года.

Есть другой вариант того-же решения:

Функция ДнейВЭтомГоду()
 Попытка
   А = Дата(Год(ТекущаяДата()),2,29);
   Возврат 366;
 Исключение
   Возврат 365;
 КонецПопытки;
КонецФункции 

,проверяем наличие 29 Февраля в этом году. Если возникает исключение, значит в этом году 365 дней, иначе — 366.

Есть еще вариант с делением года на 4. Если он делится без остатка, то год високосный. Но там есть исключения для некоторых годов. Ну и самый тупой 😉 способ определить сколько дней в году:

Сколько дней в году
1 10 11 12 13 14 53