Introduction
Le chargement d’une sauvegarde d’une base de données d’une instance SQL
Server 2000 vers une autre instance SQL Server 2000 peut générer des
désynchronisations au niveau des utilisateurs de bases de données entre les
tables sysxlogins
(base master
) et sysusers
(base chargée).
Cet article se propose se propose de montrer comment diagnostiquer les désynchronisations de users avec SQL Server 2000 et réparer ce problème.
Contexte de la désynchronisation
Dans le contexte de cet article, nous disposons de 2 instances SQL Server
2000 CGC_T1_MSQ
et CGC_T2_MSQ
pour lesquels les logins n’ont pas été créés dans
le même ordonnancement pour les bases de données pubs
et Northwind
:
Instance CGC_T1_MSQ | Instance CGC_T2_MSQ | ||
---|---|---|---|
Login (base de données par déf.) Sid | Nom en base Rôle | Login (base de données par déf.) Sid | Nom en Base Rôle |
Northwind_dbo (Northwind) 0xE6940D5E39A94E4F8533BC55968C492E | Northwind_dbo dbowner | Northwind_dbo (Northwind) 0x3D6289E5AEDB7A46A3D0385CFAD59957 | Northwind_dbo dbowner |
Northwind_reader (Northwind) 0x33CFB95F2391BF46B1A9842D0AE6A458 | northwind_reader db_datareader | Northwind_reader (Northwind) 0x95A255613027CB499E28E33CABAAFC9A | northwind_reader db_datareader |
Northwind_user1 (Northwind) 0x88B3455D93465943B0EC028023BC015C | Northwind_user1 Northwind_role | Northwind_user1 (Northwind) 0x0E07DB739970F441BD824D98C632B2B2 | Northwind_user1 / |
Northwind_user2 (Northwind) 0x5A1FAC8F0F2B514B956A7F9B5D2CFC73 | user2 public | / | / |
Northwind_writer (Northwind) 0x585AE7E89C44244E882F7C6AA40654BE | northwind_writer db_datawriter | Northwind_writer (Northwind) 0xC5C35D89BBE6FE47969536A8B0B3B944 | northwind_writer db_datawriter |
pubs_dbo (pubs) 0xCA92C84CBFD92A4887B87AA47A49E0D0 | pubs_dbo dbowner | pubs_dbo (pubs) 0x2E2225C98DF33A49BEE81D255A34E496 | pubs_dbo dbowner |
pubs_reader (pubs) 0xB5FF396C7862BE47ADC04FC807D3209D | pubs_reader db_datareader | pubs_reader (pubs) 0x23DE9F5E2F266F48930BF7B7AE31C9AB | pubs_reader db_datareader |
pubs_user1 (pubs) 0xFED6CDD7872711469B3D13E746356A89 | pubs_user1 pubs_role | pubs_user1 (pubs) 0xD23BEEA30AB67F4ABB790426EC193FE8 | pubs_user1 pubs_role |
pubs_user2 (pubs) 0x1A60B3053B39BB449D12E88FC7AF8E76 | user2 public | / | / |
pubs_writer (pubs) 0x40A72DC3C0E56D46A482B2859EBCD5FC | pubs_writer db_datawriter | pubs_writer (pubs) 0x904030F97740394687D2594F724D33EF | pubs_writer db_datawriter |
Parmi les points à noter :
- les logins
Northwind_user2
etpubs_user2
n’ont pas été définis dans le serveur de destinationCGC_T2_MSQ
- le rôle
Northwind_role
n’a pas été créé et attribué au userNorthwind_user1
dans le serveur de destinationCGC_T2_MSQ
.
Avec cette configuration concernant les logins et users :
- une sauvegarde de la base
Northwind
sur le serveurCGC_T1_MSQ
est restaurée sur le serveurCGC_T2_MSQ
. - une sauvegarde de la base
pubs
sur le serveurCGC_T1_MSQ
est restaurée sur le serveurCGC_T2_MSQ
.
restore database pubs from disk='C:\sdata\mssql\MSSQL$CGC_T1_MSQ\BACKUP\pubs\pubs_db_200410221604.dmp' go
Processed 208 pages for database 'pubs', file 'pubs' on file 1. Processed 1 pages for database 'pubs', file 'pubs_log' on file 1. RESTORE DATABASE successfully processed 209 pages in 0.393 seconds (4.340 MB/sec).
restore database Northwind from disk='C:\sdata\mssql\MSSQL$CGC_T1_MSQ\BACKUP\Northwind\Northwind_db_200410221604.dmp' go
Processed 384 pages for database 'Northwind', file 'Northwind' on file 1. Processed 1 pages for database 'Northwind', file 'Northwind_log' on file 1. RESTORE DATABASE successfully processed 385 pages in 0.750 seconds (4.197 MB/sec).
Diagnostic des désynchronisations
Procédure stockée sp_change_users_login
La procédure stockée sp_change_users_login
de la base master
permet de
diagnostiquer rapidement les désynchronisations de users dans une base de
données :
Syntaxe :
exec sp_change_users_login @Action='Report'
Désynchronisation dans la base Northwind
use Northwind go exec sp_change_users_login @Action='Report' go
UserName UserSID -------------------- ------------------------------------------------------ dbo 0xE6940D5E39A94E4F8533BC55968C492E Northwind_reader 0x33CFB95F2391BF46B1A9842D0AE6A458 Northwind_user1 0x88B3455D93465943B0EC028023BC015C Northwind_writer 0x585AE7E89C44244E882F7C6AA40654BE user2 0x5A1FAC8F0F2B514B956A7F9B5D2CFC73
La procécudre stockée sp_change_users_login
montre clairement que ce sont
les SID du serveur CGC_T1_MSQ
qui ont été remontés dans la table sysusers
de la
base Northwind
, provoquant ainsi des désynchronisations.
Pour ce qui concerne le user Northwind_user1
dans la base Northwind
, cet
utilisateur a bien le rôle Northwind_role
alors que ce dernier rôle n’existait
pas dans la base Northwind
avant rechargement, ainsi les rôles définis dans les
bases de données sont bien réattribués utilisateurs lors d’un rechargement :
use Northwind go exec sp_helpuser 'Northwind_user1' go
UserName GroupName LoginName DefDBName UserID SID -------- --------- --------- --------- ------ -------------- Northwind_user1 Northwind_role NULL NULL 7 0x88B3455D93465943B0EC028023BC015C
Désynchronisation dans la base pubs
use pubs go exec sp_change_users_login @Action='Report' go
UserName UserSID ------------- ---------------------------------------- dbo 0xCA92C84CBFD92A4887B87AA47A49E0D0 pubs_reader 0xB5FF396C7862BE47ADC04FC807D3209D pubs_user1 0xFED6CDD7872711469B3D13E746356A89 pubs_writer 0x40A72DC3C0E56D46A482B2859EBCD5FC user2 0x1A60B3053B39BB449D12E88FC7AF8E76
La procécudre stockée sp_change_users_login
montre clairement que ce sont
les SID du serveur CGC_T1_MSQ
qui ont été remontés dans la table sysusers
de la
base pubs
.
Réparation de la désynchronisation
La procédure stockée sp_change_users_login
va permettre de resynchroniser
efficacement les logins et les users dans les bases de données Northwind
et
pubs
.
Trois cas de figure vont se présenter :
- le user est le dbo de la base de données
- les logins et users ont le même nom
- le login n’existe pas pour un user dans la base de données.
Syntaxes de réparation avec sp_change_users_login
Deux syntaxes sont à notre disposition avec la procédure stockée
sp_change_users_login
pour resynchroniser les logins avec les users :
Syntaxes :
exec sp_change_users_login @Action='Auto_Fix', @UserNamePattern='user'
exec sp_change_users_login @Action='Update_One', @UserNamePattern='user', @LoginName='login'
Quelle que soit l’option (Auto_Fix
ou Update_One
), la resynchronisation ne
peut être réalisée pour le dbo d’une base et le message n°15287 est affiché.
Pour le dbo, la procédure stockée sp_changedbowner
doit être utilisée.
Exemple :
use pubs go exec sp_change_users_login @Action='Update_One', @UserNamePattern='dbo', @LoginName='pubs_dbo' go
Server: Msg 15287, Level 16, State 1, Procedure sp_change_users_login, Line 38 Terminating this procedure. 'dbo' is a forbidden value for the login name parameter in this procedure.
Option Auto_Fix
L’option Auto_Fix
est généralement utilisée lorsque le nom du login est le
même que le nom du user dans la base de données. C’est par exemple le cas du
user pubs_reader
à resynchroniser avec le login pubs_reader
sur le serveur
CGC_T2_MSQ
:
use pubs go exec sp_change_users_login @Action='Auto_Fix', @UserNamePattern='pubs_reader' go
The row for user 'pubs_reader' will be fixed by updating its login link to a login already in existence. The number of orphaned users fixed by updating users was 1. The number of orphaned users fixed by adding new logins and then updating users was 0.
Le message retourné par la procédure sp_change_users_login
indique que la
table sysusers
a été mise à jour pour le user pubs_reader
afin de
resynchroniser ce dernier user avec le login pubs_reader
.
Lorsque le login du même nom que le user n’existe pas, le
login est automatiquement créé avec l’option Auto_Fix
. Ce comportement est
particulièrement dangereux et une attention toute particulière doit y être
apportée. Il est préférable de ne pas lancer l’option 'Auto_Fix
' pour un user
n’ayant pas de login correspondant du même nom.
C’est le cas par exemple dans la base pubs
du user user2
qui est normalement
relié au login pubs_user2
. Avec la commande
Auto_Fix
, le login user2
est automatiquement créé sans mot de passe :
use pubs go exec sp_change_users_login @Action='Auto_Fix', @UserNamePattern='user2' go
New login created. Barring a conflict, the row for user 'user2' will be fixed by updating its link to a new login. Consider changing the new password from null. The number of orphaned users fixed by updating users was 0. The number of orphaned users fixed by adding new logins and then updating users was 1.
Option Update_One
L’option Update_One
est utilisée lorsque le nom du login n’est pas le même
que le nom du user dans la base de données. C’est le cas par exemple du user
user2
dans la base pubs qui doit être relié au login pubs_user2
, or ce dernier
login n’existe pas sur CGC_T2_MSQ
.
Après création du login pubs_user2, pour synchroniser le user2 avec le login pubs_user2 :
use pubs
go
exec sp_change_users_login @Action='Update_One',
@UserNamePattern='user2',
@LoginName='pubs_user2'
go
Exemple de réparation de la désynchronisation de la base Northwind
Première étape : réattribution du dbo de la base Northwind
use Northwind
go
exec sp_changedbowner 'Northwind_dbo'
go
Seconde étape :
resynchronisation des users disposant d’un login ayant le même nom avec
l’option Auto_Fix
de la procédure stockée sp_change_users_login
exec sp_change_users_login @Action='Auto_Fix',
@UserNamePattern='Northwind_reader'
go
exec sp_change_users_login @Action='Auto_Fix',
@UserNamePattern='Northwind_writer'
go
exec sp_change_users_login @Action='Auto_Fix',
@UserNamePattern='Northwind_user1'
go
Troisième étape :
resynchronisation des users ne disposant d’un login ayant le même nom avec
l’option Update_One
de la procédure stockée sp_change_users_login
.
Il est nécessaire de remapper le user user2
dans la base Northwind
avec le
login orthwind_user2
qui doit être recréé sur le serveur CGC_T2_MSQ
.
use Northwind
go
exec sp_change_users_login @Action='Update_One',
@UserNamePattern='user2',
@LoginName='Northwind_user2'
go
Dernière étape :
Vérification que tous les users sont resynchronisés avec l’option 'Report
' de
la procédure stockée sp_change_users_login
.
Procédures automatiques de synchronisation
Des procédures automatiques peuvent être facilement développées afin d’alléger le travail de resynchronisation des users avec les logins, en voici deux exemples :
sp__dba_get_orphanedusers
sp__dba_resync_users
Ces procédures peuvent être créées dans une base quelconque (msdb
par
exemple). sp__dba_get_orphanedusers
doit être créé avant
sp__dba_resync_users
.
sp__dba_get_orphanedusers
La procédure stockée sp__dba_get_orphanedusers
permet d’obtenir un état
rapide sur les users désynchronisés dans une base de données :
Syntaxe :
exec sp__dba_get_orphanedusers '<db_name>'
Exemples :
exec sp__dba_get_orphanedusers 'master' go
Getting orphaned users for database [master]... No Orphaned users in the database [master]
exec sp__dba_get_orphanedusers 'Northwind'
Getting orphaned users for database [Northwind]... Orphaned users are detected in the database [Northwind] name ---------------------------------------------------------------------------------- dbo Northwind_reader Northwind_user1 Northwind_writer user2
exec sp__dba_get_orphanedusers 'pubs66'
Database name pubs66 is not a valid user database
sp__dba_resync_users
La procédure stockée sp__dba_resync_users
resynchronise des users avec des
logins dans une base de données utilisateurs.
- La resynchronisation dans les bases de données système
master, msdb, model
ettempdb
n’est pas autorisée. - Un user d’une base de données est automatiquement mappé à un login du
même nom, en d’autres termes la procédure stockée
sp_change_users_login
n’est appelée avec l’optionAuto_Fix
que si un login du même nom que le user existe. - Des avertissements sont donnés à l’issue de l’exécution pour des users ne disposant pas de logins du même nom ainsi que pour le dbo qui n’a pu être remappé à un login.
Syntaxe :
exec sp__dba_resync_users '<db_name>'
Exemples :
exec sp__dba_resync_users 'tempdb'
Database name tempdb is a system database, this stored procedure can't be used for these ones
exec sp__dba_resync_users 'pubs66'
Database name pubs66 is not a valid user database
exec sp__dba_resync_users 'Northwind'
Getting orphaned users for database [Northwind]... Orphaned users are detected in the database [Northwind] 5 users should be resync. in database [Northwind] Starting Resync. Trying to resync user [dbo] in database [Northwind]... --> Resync is not allowed for the database owner, instead execute the stored procedure sp_changedbowner Trying to resync user [Northwind_reader] in database [Northwind]... The row for user 'Northwind_reader' will be fixed by updating its login link to a login already in existence. The number of orphaned users fixed by updating users was 1. The number of orphaned users fixed by adding new logins and then updating users was 0. Trying to resync user [Northwind_user1] in database [Northwind]... The row for user 'Northwind_user1' will be fixed by updating its login link to a login already in existence. The number of orphaned users fixed by updating users was 1. The number of orphaned users fixed by adding new logins and then updating users was 0. Trying to resync user [Northwind_writer] in database [Northwind]... The row for user 'Northwind_writer' will be fixed by updating its login link to a login already in existence. The number of orphaned users fixed by updating users was 1. The number of orphaned users fixed by adding new logins and then updating users was 0. Trying to resync user [user2] in database [Northwind]... --> User [user2] can not be mapped with a login with the same name, resync will not be done End Resync. Verifying Resync. ------------------------------------------------------------------------------- dbo : KO, Use sp_changedbowner to resync. user2 : KO, Could not be mapped to an existing login
Code source sp__get_orphanedusers
IF exists (SELECT 1 FROM sysobjects where type='P' and name = 'sp__dba_get_orphanedusers')
BEGIN
drop procedure sp__dba_get_orphanedusers
END
go
create procedure sp__dba_get_orphanedusers @dbname varchar(30)=NULL,
@action varchar(10) = 'Report'
AS
/**
* Get Orphaned Users
*
* Usage : sp__dba_get_ophanedusers @dbname (user database),@action (Report | Resync)
* Date : 10.2004
* Author : Stephane PAQUOT
*
* Version : 1.0
*/
SET NOCOUNT ON
DECLARE @cmd varchar(256),
@countOrphanedUsers integer
IF @action is NULL
BEGIN
SELECT @action='Report'
END
/**
* Checking parameter @action
*/
IF @action NOT IN ('Report','Resync')
BEGIN
PRINT 'The parameter action is invalid : Report or Resync'
RETURN -1
END
/**
* Checking parameter @dbname is supplied
*/
IF @dbname is null
BEGIN
PRINT 'Parameter Database Name @dbname is not supplied'
RETURN -1
END
/**
* Checking database name is valid
*/
IF NOT EXISTS (SELECT 1 FROM master..sysdatabases where name=@dbname)
BEGIN
PRINT 'Database name '+@dbname +' is not a valid user database'
RETURN -1
END
/**
* Checking this stored procedure is not invoked against master, model or msdb for resync option
*/
IF @action='Resync'
BEGIN
IF @dbname in ('master','model','msdb','tempdb')
BEGIN
PRINT 'Database name '+@dbname +' is a system database, this stored procedure can''t be used for these ones'
RETURN -1
END
END
/**
* Getting not valid users in a database
*/
PRINT 'Getting orphaned users for database ['+ @dbname +']...'
BEGIN
SET ANSI_WARNINGS OFF
IF @action='Report'
BEGIN
CREATE TABLE #_sys_OrphanedUsers (
name sysname,
sid varbinary
)
END
SELECT @cmd = 'SELECT name, sid FROM [' + @dbname + ']..sysusers where issqluser = 1 and (sid is not null and sid <> 0x0) and suser_sname(sid) is null order by name'
INSERT INTO #_sys_OrphanedUsers
EXEC (@cmd)
SET ANSI_WARNINGS ON
SELECT @countOrphanedUsers = count(1) FROM #_sys_OrphanedUsers
IF @countOrphanedUsers > 0
BEGIN
PRINT 'Orphaned users are detected in the database ['+ @dbname +']'
IF @action='Report'
BEGIN
SELECT name FROM #_sys_OrphanedUsers
DROP TABLE #_sys_OrphanedUsers
END
ELSE
BEGIN
RETURN @countOrphanedUsers
END
END
ELSE
BEGIN
PRINT 'No Orphaned users in the database ['+ @dbname +']'
RETURN 0
END
END
Code source sp__dba_resync_users
IF exists (SELECT 1 FROM sysobjects where type='P' and name = 'sp__dba_resync_users')
BEGIN
drop procedure sp__dba_resync_users
END
go
create procedure sp__dba_resync_users @dbname varchar(30)=NULL
AS
/**
* Resync users on user databases after loading
* - no changes are allowed for system databases
* - no changes are made for the dbo
* - no changes are made for users not mapped with a login with the same name
*
* Usage : sp__dba_resync_users @dbname (user database)
* Date : 10.2004
* Author : Stephane PAQUOT
*
* Version : 1.0
*/
SET NOCOUNT ON
DECLARE @cmd varchar(256),
@uname sysname,
@usid varbinary,
@retval integer,
@retstring varchar(20)
CREATE TABLE #_sys_OrphanedUsers (
name sysname,
sid varbinary
)
CREATE TABLE #tmpResults (
name sysname,
reason varchar(2),
action varchar(60)
)
EXEC @retval = sp__dba_get_orphanedusers @dbname,'Resync'
IF @retval > 0
BEGIN
SELECT @retstring = convert(varchar(20),@retval)
PRINT @retstring + ' users should be resync. in database ['+ @dbname + ']'
PRINT ''
PRINT 'Starting Resync.'
PRINT ''
DECLARE c_orphanedUsers cursor global for
SELECT sid,name FROM #_sys_OrphanedUsers
OPEN c_orphanedUsers
FETCH NEXT FROM c_orphanedUsers INTO @usid,@uname
WHILE (@@FETCH_STATUS <> -1)
BEGIN
PRINT 'Trying to resync user ['+ @uname+'] in database [' + @dbname + ']...'
/** Checking dbo */
IF @uname = 'dbo'
BEGIN
INSERT INTO #tmpResults VALUES ('dbo','KO','Use sp_changedbowner to resync.')
PRINT '--> Resync is not allowed for the database owner, instead execute the stored procedure sp_changedbowner'
END
ELSE
BEGIN
IF NOT EXISTS (SELECT 1 FROM master..syslogins WHERE name=@uname)
BEGIN
INSERT INTO #tmpResults VALUES (@uname,'KO','Could not be mapped to an existing login')
PRINT '--> User ['+ @uname +'] can not be mapped with a login with the same name, resync will not be done'
END
ELSE
BEGIN
SELECT @cmd = 'use [' + @dbname + '] exec sp_change_users_login @Action=''Auto_Fix'', @UserNamePattern='''+ @uname +''''
EXEC (@cmd)
END
END
FETCH NEXT FROM c_orphanedUsers INTO @usid,@uname
END
CLOSE c_orphanedUsers
DEALLOCATE c_orphanedUsers
PRINT ''
PRINT 'End Resync.'
PRINT ''
PRINT ''
PRINT 'Verifying Resync.'
PRINT ''
SELECT name + ' : ' + reason + ', ' + action from #tmpResults
END
DROP TABLE #_sys_OrphanedUsers
DROP TABLE #tmpResults
go