I would like to list all users on a specific database.
When I use the sys.server_principals or sys.sysmembers system views it does not list all the users that appear under the Security -> Users list in the Object Browser in SQL Server Management Studio.
Here is what I attempted:
USE [Database_Name];
SELECT user_name([memberuid]) as [Username]
FROM [sys].[sysmembers];
USE [Database_Name];
SELECT [name]
FROM [sys].[server_principals];
Thanks!
You should not be using sysmembers or sysusers for this - all the sys___ views are are backward compatibility views and (a) will be dropped from the product at some point and (b) won't necessarily contain current information.
If you are looking for database principals, don't look in sys.server_principals, look in sys.database_principals.
USE your_database;
GO
SELECT name, create_date, type_desc
FROM sys.database_principals
WHERE is_fixed_role = 0;
This will include things like guest, INFORMATION_SCHEMA, dbo, public and sys. However it won't include accounts like sa which have inherent rights over the database without needing an explicit principal defined.
Related
Server-scoped Dynamic Management View are stored only in the Master database.
SELECT name, type, type_desc
FROM sys.system_objects
WHERE name LIKE 'dm%'
ORDER BY name
How to list Database-Scoped DM Views and where it stored?
Can you advise?
Server-scoped Dynamic Management View are stored only in the Master
database.
Not exactly. DMVs are stored in the internal mssqlsystemresource database but are visible in all databases. You should get the same results if you run your query from any database, assuming you're not limited by permissions.
Database-scoped DMVs generally have prefix 'dm_db_' and can be listed with the query below.
SELECT name, type, type_desc
FROM sys.system_objects
WHERE name LIKE N'dm%[_]db[_]%'
ORDER BY name;
Database names and logins are anonymized in what follows. There are some answers
on SO that are similar to this situation, but not exactly the same, hence my question.
Attempting to deploy an assembly to production database FOO_PROD fails with message:
Msg 33009, Level 16, State 2, Line 17
The database owner SID recorded in the master database differs from the
database owner SID recorded in database 'FOO_PROD'. You should correct
this situation by resetting the owner of database 'FOO_PROD' using the
ALTER AUTHORIZATION statement.
Indeed, the following two queries demonstrate the difference in SID.
First, we look at the SID of FOO_PROD:
SELECT SD.[SID],
SL.Name as [LoginName]
FROM master..sysdatabases SD INNER JOIN master..syslogins SL
on SD.SID = SL.SID
WHERE SD.Name = 'FOO_PROD'
which shows the result of:
SID, LoginName
0x010500000000000515000000C4B7E63D99D15C20332A47A24B100000, BATZ\boink
Second, we look at the SID of FOO_PROD in the master database:
SELECT SD.[SID],
SL.Name as [LoginName]
FROM master..sysdatabases SD INNER JOIN master..syslogins SL
on SD.SID = SL.SID
WHERE SD.Name = 'master'
which shows the result of:
SID, LoginName
0x01, [sa]
We notice that indeed, just as Visual Studio complained, the SIDs do not
match. They must be made to match in order to proceed (apparently).
Constraints: The SID on FOO_PROD cannot be changed because several other systems
that use the database expect it to have the SID and LoginName it currently has.
Question 1: Is the solution then to change the SID, LoginName on the master database? Would
it hurt anything or be a bad idea to do so?
Say you respond that it is ok to change the SID, LoginName on master, then,
how does one make the change to the 'master' database? Well, I've
not done it before, but candidate solutions and commentary can be found here:
The database owner SID recorded in the master database differs from the database owner SID
However, this situation is different from those presented in the link above, I
think, in that the change must happen to/on the master database ala:
exec sp_changedbowner [BATZ\boink]
Question 2: Is that the correct way to do it?
Naturally I'll check with stakeholders if such a change to master database will
cause undesired outcomes, but I hope to get some guidance here before I even
check on that.
Update based on #srutzky's updated answer:
-- Step 1
SELECT sd.[name], sd.[owner_sid], sp.[name]
FROM sys.databases sd
INNER JOIN sys.server_principals sp
ON sp.[sid] = sd.[owner_sid]
WHERE sd.[name] = N'FOO_PROD';
returns:
name, owner_sid, name
FOO_PROD, 0x010500000000000515000000C4B7E63D99D15C20332A47A24B100000, BATZ\boink
Then
-- Step 2
USE [FOO_PROD];
SELECT dp.[sid], sp.[name]
FROM sys.database_principals dp
INNER JOIN sys.server_principals sp
ON sp.[sid] = dp.[sid]
WHERE dp.[name] = N'dbo';
returns:
sid, name
0x01, sa
Yes, the SIDs really do need to match as a mismatch is an indication of a potentially harmful DB being restored to the instance; this is a safe-guard.
BUT, first we need to know exactly what we are looking at. While there is definitely a mismatch in the owner SIDs between the record in FOO_PROD and the record in master (hence the error message), your queries are not looking at the value in FOO_PROD. Your two queries are looking at the value in master for the owner of FOO_PROD, and in master (again) for the owner of master (entirely irrelevant here), respectively.
Step 1
Do not use sys* objects for anything as those are compatibility Views so that older stuff written for SQL Server 2000 and prior still work (well, dbo.sys* tables in msdb are still valid). Starting with SQL Server 2005, only sys.* objects (no need to specify master.) should be used. Meaning, use:
SELECT sd.[name], sd.[owner_sid], sp.[name]
FROM sys.databases sd
INNER JOIN sys.server_principals sp
ON sp.[sid] = sd.[owner_sid]
WHERE sd.[name] = N'FOO_PROD';
Step 2
You need to check the value IN the database itself for the owner's SID as it has it recorded, which is not in sys.databases (or even in master..sysdatabases). When checking the Database's value for it's owner, you need to look in sys.database_principals for the dbo User as follows:
USE [FOO_PROD];
SELECT dp.[sid], sp.[name]
FROM sys.database_principals dp
INNER JOIN sys.server_principals sp
ON sp.[sid] = dp.[sid]
WHERE dp.[name] = N'dbo';
Step 3
Using sp_changedbowner is required if you are on SQL Server 2005, but starting with SQL Server 2008 that stored procedure is deprecated in favor of the newer ALTER AUTHORIZATION (though it still works). But yes, this is the way to make them the same as it will sync both locations to whichever Login you specify.
However, you need to make sure that BATZ\boink is a valid Windows Login for the domain that the SQL Server instance belongs to, AND that this particular Windows Login has an SID of 0x010500000000000515000000C4B7E63D99D15C20332A47A24B100000. If the Login does not exist, hopefully you will be able to create it via CREATE LOGIN.
Since the owner in the database is SA, and you want to change the owner recorded in Master to SA, just run
alter authorization on database::[foo_prod] to sa
What's the way to query more than one database using SQL*Plus?
In MySQL it's possible to do something like this :
create table WK_LINK_JOINT_IDEOREQ AS
select k.constraint_name cn, k.table_name tl, l.column_name lc
, k.referenced_table_name tg, k.column_name cg, l.referenced_table_name td
, l.referenced_column_name cd
from information_schema.KEY_COLUMN_USAGE k
It's just an example, it's not complete : but as you can see, we are working on two databases, INFORMATION_SCHEMA and another one.
I want to do somthing like this using SQL*Plus, but the problem is that when we connect using SQL*Plus, we specify the database (SID) which means that the others are not accessible.
Is there a way to do it?
Oracle has a different interpretation of DATABASE from MySQL. In Oracle we have multiple users or schemas in the same database.
So, if what you really want is to access objects from a different schema all that has to happen is for that schema to grant you privileges. You can then reference the tables (or whatever) in your SQL.
User JOE grants you select on his table
SQL> conn JOE/SOAP
SQL> grant select on my_table to ABC;
You can then run queries on it:
SQL> conn ABC/DEF
SQL> select * from joe.my_table;
In your example you used INFORMATION_SCHEMA. The Oracle equivalent of this is the data dictionary, an enormous library of views. Find out more.
Public access is granted by default on most of them. So you can select from USER_TABLES, USER_CONSTRAINTS and USER_CONS_COLUMNS to re-create that query (assuming I have understood it correctly).
I have an application that lets users browse data in a SQL database, and I'd like to warn the user if they don't have rights to see every table. I'm getting a list of tables by doing a SELECT on sys.objects, but if the user doesn't have SELECT rights on a table, it doesn't show up in that query, so there's no way I can see to know what I don't know.
I don't even need to know what in particular I'm missing, just if there's anything the user can't view. Anybody know how to accomplish this?
CLARIFICATION:
I'm creating a tool that can be run against any SQL Server, so I'm not looking to set up permissions to see every object - I just want to know if the current user can. If they don't have rights, that's fine - I just want to make sure they know that, since the results they get won't reflect the entire database.
I'm not exactly sure if this is in line with any security concerns of yours, but my idea would be to create a user called SELECT_ALL that has select rights to all tables, create a connection with SELECT_ALL and query the sys.objects table, pass the list of tables (or just a count(*) if you want to simply know if the user has complete access or anything less than complete), close the connection, then repeat with the user's credentials and compare.
You need to mask the sys.objects query with a user with different permissions that can see the lot. The best way is EXECUTE AS OWNER in a view or udf. This enumerates all objects (if dbo owns everything) thus allowing you to find out what is missing (which is masked by metadata visibility)
CREATE VIEW dbo.mymask
WITH EXECUTE AS OWNER --the magic
AS
SELECT * FROM sys.objects
GO
SELECT
*
FROM
dbo.mymask v
LEFT JOIN
sys.objects o2 ON v.object_id = o.object_id
If you use roles, or want to do it in one then something like
CREATE VIEW dbo.mymask
WITH EXECUTE AS OWNER --the magic
AS
SELECT ...
FROM
sys.objects O
JOIN
sys.database_permissions DP ON ... --premissions
JOIN
sys.database_principals R ON DP. = R --role
etc to role members etc
WHERE
xxx.name = ORIGINAL_LOGIN()
GO
Does SQL store any information about who originally created a view, or who last modified it?
It's too late now, but if you were using 2008 you could create an audit that will track future changes.
EDIT: found it!
select p.name, v.*
from sys.all_views v, sys.database_principals p, sys.schemas s
where p.principal_id = s.principal_id
and v.schema_id = s.schema_id
and v.name = 'your_view_name'
This will produce a number of interesting details about the views in your database, including the column principal_id. Join with sys.database_principals on principal_id for the username!
I am not sure if there is a way to see who created the view but sp_help will get you some information on when it was created
sp_help viewMyView
sp_help works on any and all database objects BTW.
SQL Server does not store explicit information about who created or modified an object. There is information in the metadata catalog about who is the owner of a given object, or to what schema does the object belong to:
select * from sys.objects where object_id = object_id('<object name>');
Depending on the object type either the principal_id is populated with the database principal ID of the owner, or the schema_id is populated with the Id of the schema to which the object belongs. All schemas have an owner and which can be retrieved from the metadata catalog:
select * from sys.schemas
However note that these will only reveal the owner of the object. The owner does not necessarily means the user that created it or modified it. Ownership of objects can be changed during creation or after creation with the ALTER AUTHORIZATION statement, making identification by ownership unreliable at best. Also all members of sysadmin role map to the same database principal, dbo, in every database.
To properly identify the the user that created an object you should deploy auditing methods, but that require prior deployment of the audit. Some forensics can be done after the fact if audit was not deployed:
You can dig into the log file, Paul Randal has an example in his recent blog.
You can look into the default trace