Запрет открытия нескольких сессий в Navision при помощи SQL Server

Прямо скажем, этот вопрос возникает очень часто – «Как запретить одному и тому же пользователю открывать несколько окон Navision одновременно?” Обычно вопрос связан с ограничением по количеству сессий, ну или еще с чем-нибудь.

Пройдясь по основным форумам, вам подскажут несколько подходов для реализации этой задачи – от использования сторонних приложений (для завершения процессов), до отправки Alt+F4 окну Navision с помощью «Windows Scripting Host», чтобы вручную убить сессию в “Session Monitor»’е (Navision) или «Activity Monitor»’е (SQL Server).

Я хочу показать еще один – на мой взгляд, очень удобный – способ сделать это, используя не очень хорошо известную хранимую процедуру SQL – «sp_$ndo$loginproc». Это процедура для Навижна (по-моему, начиная с 4-й версии), которая не устанавливается из коробки, но легко может быть создана вручную (это описано в документации, только не помню, в каком именно PDF-е, извините :)

USE [Navision]
GO
CREATE PROCEDURE [sp_$ndo$loginproc]
@appname VARCHAR(64) = NULL,
@appversion VARCHAR(16) = NULL
AS
BEGIN
-- do whatever you want to do
END
GO
GRANT EXECUTE ON [sp_$ndo$loginproc] TO [public]
GO

Процедура вызывается из клиента C/SIDE при подключении к SQL Server’у; то есть, это некоторым образом процедура «автозапуска». Следовательно, все что нам нужно – набросать в ней несколько строчек на T-SQL для того, чтобы 1) проверить логин и 2)если требуется, запретить доступ.

USE [Navision]
GO
CREATE PROCEDURE [sp_$ndo$loginproc]
@appname VARCHAR(64) = NULL,
@appversion VARCHAR(16) = NULL
AS
BEGIN
DECLARE @login_count INT
IF EXISTS(
SELECT ses.[login_name] FROM sys.dm_exec_sessions (nolock) ses
LEFT JOIN master..sysprocesses (nolock) pro ON pro.[spid] =  ses.[session_id]
WHERE ses.[program_name] IN ('Microsoft Business Solutions-Navision client', 'Microsoft Dynamics NAV client')
AND pro.[dbid] = db_id()
AND UPPER(ses.[login_name]) = UPPER(suser_name())
GROUP BY ses.[login_name]
HAVING COUNT(*) > 1  -- change threshold if neccessary
) BEGIN
SELECT @login_count = COUNT(*) - 1 FROM sys.dm_exec_sessions (nolock) ses
LEFT JOIN master..sysprocesses (nolock) pro ON pro.[spid] =  ses.[session_id]
WHERE ses.[program_name] IN ('Microsoft Business Solutions-Navision client', 'Microsoft Dynamics NAV client')
AND pro.[dbid] = db_id()
AND UPPER(ses.[login_name]) = UPPER(suser_name())
GROUP BY ses.[login_name]
HAVING COUNT(*) > 1  -- change threshold if neccessary
raiserror ('You are already logged on %i time(s) to the system. Access is denied. Contact your System-Administrator.', 18, 1, @login_count)
RETURN
END
END
GO
GRANT EXECUTE ON [sp_$ndo$loginproc] TO [public]
GO

Этот скрипт написан для SQL Server 2005, но, в принципе, в случае необходимости его легко переработать для SQL Server 2000. Некоторые вещи, на которые стоит обратить внимание:

  • «Program Name» зашито в коде и должно быть изменено, если поменяется имя Navision-приложения;
  • В моем пример количество доступных сессий – одна; при втором логине выскочит ошибка. Порог легко изменить, а также, если потребуется, порог можно установить для отдельных пользователей, например вот так:
DECLARE @max_logins INT
SET @max_logins = 1
SELECT @max_logins = [Max_ Logins] FROM [dbo].[CRONUS$User Setup] WHERE [USER ID] = UPPER(suser_name)

(Здесь поле «Max. Logins» должно быть добавлено в таблицу «User Setup»)

  • Тогда сравнение будет таким:
HAVING COUNT(*) > @max_logins

Что происходит в этом случае: когда пользователь входит в систему, выполняется хранимая процедура. Если количество доступных сессий превышено, процедура вылетает с ошибкой – которая видна в NAV, и, естественно, отменяет процесс входа в систему, таким образом, клиент ОТКЛЮЧАЕТСЯ, и не тратит драгоценную лицензию.

Как всегда, буду благодарен за комментарии и добавления/улучшения в коде!

P.S. Извините, забыл упомянуть следующее: для чтения из таблицы sysprocesses и sys.dm_exec_sessions у пользователя должны быть включены права на VIEW SERVER STATE, роль public не включает в себя эти права, так что, чтобы наш контроль сессий заработал, их надо добавить вручную:

USE [master]
GO
GRANT VIEW SERVER STATE TO PUBLIC
GO

Оригинал заметки можно прочитать здесь:
http://dynamicsuser.net/blogs/stryk/archive/2008/12/10/prevent-multiple-logins-in-nav-with-sql-server.aspx

Автор:

В области Navision - с 2003 года. Профессиональные интересы: NAV, MS SQL, .NET, BPMN, IT-менеджмент. Предметная область: логистика, финансы, склады, 3PL.

Количество статей, опубликованных автором: 86.

Добавить комментарий