Ok, algum tempo atrás, eu postei sobre como você pode impor limites às conexões do SQL Server utilizando o Resource Governor. Mas, e se você não pode utilizar ele? Você sempre pode bloquear logins usando uma trigger, mas eu não gosto da ideia de ter um select rodando a cada login. Então, eu cheguei a esta solução, utilizando uma stored procedure, uma tabela e o Agent.
A ideia principal é armazenar o horário em que um usuário deve ser bloqueado pelo Agent. Abaixo você pode ver a criação da tabela:
CREATE TABLE dbo.HorariosBloqueio (
Id INT NOT NULL,
LoginName NVARCHAR(100) NOT NULL,
HrInicio TIME NOT NULL, /* horário de inicio do bloqueio */
HrTermino TIME NOT NULL, /*horário de termino */
Bloqueado INT DEFAULT 0, /* 0 = desbloqueado, 1 = bloqueado */
PRIMARY KEY (Id)
);
GO
/* regra para Não bloquear o usuário SA */
ALTER TABLE dbo.HorariosBloqueio
ADD CONSTRAINT chk_users CHECK (LoginName not in ('sa'));
ALTER TABLE dbo.HorariosBloqueio
ADD CONSTRAINT chk_hora_final_maior CHECK (HrTermino > HrInicio);
ALTER TABLE dbo.HorariosBloqueio
ADD CONSTRAINT chk_status_bloqueio CHECK (Bloqueado in (0, 1));
CREATE SEQUENCE dbo.seq_HorariosBloqueio START WITH 1 INCREMENT BY 1;
GO
Depois de criar a tabela, vamos verificar a procedure que vai fazer todo o trabalho de habilitar/desabilitar os usuários. Fique ciente que, nesta procedure, eu defini o nome do banco onde a tabela está armazenada. Você pode substituir o nome DBATOOLS
pelo o nome da sua base.
IF OBJECT_ID('dbo.sp_ValidarLogin') IS NULL
EXEC ('CREATE PROCEDURE dbo.sp_ValidarLogin AS RETURN 0;');
GO
CREATE OR ALTER PROC dbo.sp_ValidarLogin
AS BEGIN
DECLARE @LoginName AS NVARCHAR(100);
DECLARE @Momento AS TIME;
SET @Momento = CAST(GETDATE() AS TIME);
/* Bloqueia os que ainda não estiverem bloqueados de acordo com a hora atual */
DECLARE block_cursor CURSOR
FOR SELECT LoginName FROM [DBATOOLS].[dbo].[HorariosBloqueio] WHERE Bloqueado = 0 AND HrInicio <= @Momento AND HrTermino >= @Momento
OPEN block_cursor;
FETCH NEXT FROM block_cursor INTO @LoginName
WHILE @@FETCH_STATUS = 0
BEGIN
exec ('ALTER LOGIN ' + @LoginName + ' DISABLE;');
print 'Bloqued usuario ' + @LoginName;
FETCH NEXT FROM block_cursor INTO @LoginName
END;
CLOSE block_cursor;
DEALLOCATE block_cursor;
UPDATE [DBATOOLS].[dbo].[HorariosBloqueio] SET Bloqueado = 1 WHERE HrInicio <= @Momento AND HrTermino >= @Momento
/* Libera quem estava bloqueado */
DECLARE unblock_cursor CURSOR
FOR SELECT LoginName FROM [DBATOOLS].[dbo].[HorariosBloqueio] WHERE Bloqueado = 1 AND (HrInicio > @Momento OR HrTermino < @Momento)
OPEN unblock_cursor ;
FETCH NEXT FROM unblock_cursor INTO @LoginName
WHILE @@FETCH_STATUS = 0
BEGIN
exec ('ALTER LOGIN ' + @LoginName +' ENABLE;');
print 'Unbloqued usuario ' + @LoginName;
FETCH NEXT FROM unblock_cursor INTO @LoginName
END;
CLOSE unblock_cursor;
DEALLOCATE unblock_cursor;
UPDATE [DBATOOLS].[dbo].[HorariosBloqueio] SET Bloqueado = 0 WHERE Bloqueado = 1 AND (HrInicio > @Momento OR HrTermino < @Momento)
END;
Certo, então agora tudo que temos que fazer é definir o job no Agent para rodar a procedure de minuto em minuto. Novamente, a ideia principal é chamar a procedure quando um usuário deve ser bloqueado e quando deve ser desbloqueado.
-- Vai bloquear o usuário protheus das 10 AM até 15 PM
INSERT INTO dbo.HorariosBloqueio (Id, LoginName, HrInicio, HrTermino) VALUES (NEXT VALUE FOR seq_HorariosBloqueio, 'protheus', '10:00:00', '15:00:00');