Comprobando si ya existe un inicio de sesión de SQL Server

176

Necesito verificar si ya existe un inicio de sesión específico en el SQL Server, y si no es así, debo agregarlo.

He encontrado el siguiente código para agregar realmente el inicio de sesión a la base de datos, pero quiero envolver esto en una declaración IF (de alguna manera) para verificar si el inicio de sesión existe primero.

CREATE LOGIN [myUsername] WITH PASSWORD=N'myPassword', 
DEFAULT_LANGUAGE=[us_english], 
CHECK_EXPIRATION=OFF, 
CHECK_POLICY=OFF 
GO

Entiendo que necesito interrogar una base de datos del sistema, ¡pero no estoy seguro de por dónde empezar!

Brett Rigby
fuente
10
Esta es una pregunta importante, pero como está redactado, parece perder una distinción importante: usuario vs. inicio de sesión. El posible duplicado al que Jon se vincula realmente parece ser sobre los usuarios. Esta pregunta dice "usuario" en el título, pero trata con los inicios de sesión en el código de la pregunta y en la respuesta aceptada. Edité el título y la pregunta en consecuencia.
LarsH
1
Solo para agregar al comentario de @LarsH, los inicios de sesión están asociados con una instancia del servidor SQL, y los usuarios están asociados con una base de datos específica. Los usuarios de la base de datos se pueden crear desde los inicios de sesión del servidor, por lo que tienen acceso a una base de datos específica. Vea este excelente artículo y, de hecho, toda la serie de la que forma parte (Stariway to SQL Server Security)
Ingeniero invertido

Respuestas:

141

Desde aqui

If not Exists (select loginname from master.dbo.syslogins 
    where name = @loginName and dbname = 'PUBS')
Begin
    Select @SqlStatement = 'CREATE LOGIN ' + QUOTENAME(@loginName) + ' 
    FROM WINDOWS WITH DEFAULT_DATABASE=[PUBS], DEFAULT_LANGUAGE=[us_english]')

    EXEC sp_executesql @SqlStatement
End
Johnno Nolan
fuente
66
debe usar QUOTENAME para evitar la inyección de sql. El atacante puede pasar un @loginName comox] with password ''y'';\r\ndrop table foo;\r\n
Remus Rusanu
2
¿Por qué fue necesario crear una declaración como una cadena y luego usar sp_executesql, en lugar de solo ingresar directamente CREATE LOGIN [@loginName] FROM ...? Disculpe mi ignorancia, me gustaría aprender ...
LarsH
44
@LarsH: Se requiere crear la declaración como una cadena porque CREATE LOGIN no puede usar un parámetro para el nombre de inicio de sesión, requiere un literal de cadena. No estoy seguro de por qué, pero descubrí por las malas que es cierto.
Joseph Bongaarts
@JosephBongaarts: OK, gracias. Supongo que es como los nombres de las tablas en las declaraciones SELECT. Tal vez la idea es disminuir la superficie vulnerable a los ataques, aunque no sé si ayudaría.
LarsH
1
Creo que QUOTENAME()da vueltas @loginName, no toda la declaración, y luego puedes deshacerte de los delimitadores manuales [y] @loginName.
brianary
288

Aquí hay una manera de hacer esto en SQL Server 2005 y versiones posteriores sin usar la vista obsoleta de syslogins:

IF NOT EXISTS 
    (SELECT name  
     FROM master.sys.server_principals
     WHERE name = 'LoginName')
BEGIN
    CREATE LOGIN [LoginName] WITH PASSWORD = N'password'
END

La vista server_principals se usa en lugar de sql_logins porque esta última no enumera los inicios de sesión de Windows.

Si necesita verificar la existencia de un usuario en una base de datos en particular antes de crearlos, puede hacer esto:

USE your_db_name

IF NOT EXISTS
    (SELECT name
     FROM sys.database_principals
     WHERE name = 'Bob')
BEGIN
    CREATE USER [Bob] FOR LOGIN [Bob] 
END
Derek Morrison
fuente
17
La mejor respuesta, sin SQL dinámico involucrado, ni ningún uso de vista obsoleto. ¡Gracias!
Casper Leon Nielsen
77
En el caso de SQL Azure, las dos tablas de destino son sys.sql_logins y sys.sysusers; podría ser bueno incluir esto en la respuesta.
Brett
No es útil si su script necesita usar un nombre de usuario variable.
Ross Presser
@Derek Morrison podemos agregar una condición más para SID
AstroBoy
30

Como una adición menor a este hilo, en general, desea evitar el uso de las vistas que comienzan con sys.sys * ya que Microsoft solo las incluye por compatibilidad con versiones anteriores. Para su código, probablemente debería usar sys.server_principals. Esto supone que está utilizando SQL 2005 o superior.

Bomlin
fuente
Probado, funciona y más actual que las otras respuestas. +1 a ti también.
David
Sí, con 2005 Microsoft eliminó el acceso directo a las tablas del sistema. Para evitar romper el código antiguo, incluyen vistas que tenían el mismo nombre que las tablas antiguas. Sin embargo, solo están destinados a códigos más antiguos y los códigos más nuevos deberían usar las nuevas vistas. En BOL, haga una búsqueda en las Tablas del sistema de mapeo para averiguar qué debe usar.
Bomlin
11

Puede usar la función incorporada:

SUSER_ID ( [ 'myUsername' ] )

vía

IF [value] IS NULL [statement]

me gusta:

IF SUSER_ID (N'myUsername') IS NULL
CREATE LOGIN [myUsername] WITH PASSWORD=N'myPassword', 
DEFAULT_LANGUAGE=[us_english], 
CHECK_EXPIRATION=OFF, 
CHECK_POLICY=OFF 
GO

https://technet.microsoft.com/en-us/library/ms176042(v=sql.110).aspx

Hüda
fuente
Se votó a favor de la inclusión de campos opcionales que inhabilitan la política y las comprobaciones de vencimiento.
Archibald
8

Pruebe esto (reemplace 'usuario' con el nombre de inicio de sesión real):

IF NOT EXISTS(
SELECT name 
FROM [master].[sys].[syslogins]
WHERE NAME = 'user')

BEGIN 
    --create login here
END
Bagazo
fuente
@Marc: Lo siento pero te equivocas. Table [syslogins] mantiene inicios de sesión y table [sysusers] mantiene usuarios.
abatishchev
6

Esto funciona en SQL Server 2000.

use master
select count(*) From sysxlogins WHERE NAME = 'myUsername'

en SQL 2005, cambie la segunda línea a

select count(*) From syslogins WHERE NAME = 'myUsername'

No estoy seguro acerca de SQL 2008, pero supongo que será lo mismo que SQL 2005 y, de lo contrario, esto debería darle una idea de dónde comenzar a buscar.

David
fuente
5

¿Qué es exactamente lo que quiere comprobar para iniciar sesión o usuario? se crea un inicio de sesión en el nivel del servidor y se crea un usuario en el nivel de la base de datos para que el inicio de sesión sea único en el servidor

también se crea un usuario contra un inicio de sesión, un usuario sin inicio de sesión es un usuario huérfano y no es útil ya que no puede realizar el inicio de sesión del servidor SQL sin un inicio de sesión

tal vez necesites esto

verificar el inicio de sesión

select 'X' from master.dbo.syslogins where loginname=<username>

la consulta anterior devuelve 'X' si el inicio de sesión existe, devuelva nulo

luego crea un inicio de sesión

CREATE LOGIN <username> with PASSWORD=<password>

esto crea un inicio de sesión en el servidor sql. pero solo acepta contraseñas seguras

cree un usuario en cada base de datos que desee para iniciar sesión como

CREATE USER <username> for login <username>

asignar derechos de ejecución al usuario

 GRANT EXECUTE TO <username>

DEBE TENER permisos de SYSADMIN o decir 'sa' para abreviar

puede escribir un procedimiento sql para eso en una base de datos

create proc createuser
(
@username varchar(50),
@password varchar(50)
)
as
begin
if not exists(select 'X' from master.dbo.syslogins where loginname=@username)
begin
 if not exists(select 'X' from sysusers where name=@username)
 begin
exec('CREATE LOGIN '+@username+' WITH PASSWORD='''+@password+'''')
exec('CREATE USER '+@username+' FOR LOGIN '+@username)
exec('GRANT EXECUTE TO '+@username)
end
end
end
Akshita
fuente
5

Para evitar conflictos de nombres entre inicios de sesión, roles, usuarios, etc., debe verificar la typecolumna de acuerdo con documentación de Microsoft sys.database_principals

Para manejar caracteres especiales en nombres de usuario, etc., use N'<name>'y[<name>] consecuencia.

Crear inicio de sesión

USE MASTER
IF NOT EXISTS (SELECT 1 FROM master.sys.server_principals WHERE 
[name] = N'<loginname>' and [type] IN ('C','E', 'G', 'K', 'S', 'U'))
    CREATE LOGIN [<loginname>] <further parameters>

Crear usuario de base de datos

USE <databasename>
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE 
[name] = N'<username>' and [type] IN ('C','E', 'G', 'K', 'S', 'U'))
    CREATE USER [<username>] FOR LOGIN [<loginname>]

Crear rol de base de datos

USE <databasename>
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE 
[name] = N'<rolename>' and Type = 'R')
    CREATE ROLE [<rolename>]

Agregar usuario al rol

USE <databasename>
EXEC sp_addrolemember N'<rolename>', N'<username>'

Conceder derechos al rol

USE <databasename>
GRANT SELECT ON [<tablename>] TO [<rolename>]
GRANT UPDATE ON [<tablename>] ([<columnname>]) TO [<rolename>]
GRANT EXECUTE ON [<procedurename>] TO [<rolename>]
Henrik Holmgaard Høyer
fuente
-1

Primero debe verificar la existencia de inicio de sesión utilizando la vista syslogins:

IF NOT EXISTS 
    (SELECT name  
     FROM master.sys.server_principals
     WHERE name = 'YourLoginName')
BEGIN
    CREATE LOGIN [YourLoginName] WITH PASSWORD = N'password'
END

Luego debe verificar la existencia de su base de datos:

USE your_dbname

IF NOT EXISTS
    (SELECT name
     FROM sys.database_principals
     WHERE name = 'your_dbname')
BEGIN
    CREATE USER [your_dbname] FOR LOGIN [YourLoginName] 
END
M.Alaghemand
fuente
1
No sé, decir que "tienes que verificar la existencia de inicio de sesión usando la vista syslogins", luego publicar el código que no usa esa vista parece un problema de copiar y pegar. Además, después de la primera declaración, la línea "Entonces debe verificar la existencia de su base de datos", usando un formulario paralelo, parece que le está pidiendo a alguien que verifique la existencia de una base de datos, no un usuario de nivel de base de datos. Y debe especificar que el segundo lote debe ejecutarse dentro de la base de datos de destino. En general, esta es solo una explicación muy pobre. Y como lo agregaste cinco años después de que la respuesta más votada dijera lo mismo, pero mejor ...
Riendo Vergil el