How can i create view using the SYSTEM_USER's permission path in query - sql

I am attempting to make a view of a table that uses the current user's user name (System_user) with their log in information ([permission path]) which tells me which subset of the data they are allowed to see in the view. Views cannot be created with variables or temporary tables so made an attempt at user defined functions to return a table but it appears to be a bit beyond my current skills.
It works as a Query
DECLARE #user as varchar(50)
SELECT #user = SYSTEM_USER
CREATE TABLE LoginInformation(
[account name] varchar(50),
[type] varchar(50),
[privilege] varchar(50),
[mapped login name] varchar(50),
[permission path] varchar(50)
)
INSERT LoginInformation
EXEC xp_LoginInfo #AcctName = #user, #Option = 'all';
SELECT * FROM SomeTableName
WHERE Permission IN (SELECT [permission path] FROM LoginInformation)
When creating a view I get stuck because I cannot declare a variable:
DECLARE #user as varchar(50)
SELECT #user = SYSTEM_USER
When I tried user defined functions i got held up because I could not Insert an Executed statement
INSERT LoginInformation
EXEC xp_LoginInfo #AcctName = #user, #Option = 'all';
I also do not appear to be able to do the simplest solution:
INSERT LoginInformation
EXEC xp_LoginInfo #AcctName = SYSTEM_USER, #Option = 'all';
Ideally i would have a function that took System_user as a parameter and gave me a table of just that user's [permission path]. I could then use that in my view's creation.
WHERE Permission IN (MyFunctionName(SYSTEM_USER))

xp_LoginInfo is a stored procedure. You can see its underlying code by running the following T-SQL statement:
sp_helptext xp_LoginInfo
Which produces the output:
create procedure sys.xp_logininfo
#acctname sysname = null, -- IN: NT login name
#option varchar(10) = null, -- IN: 'all' | 'members' | null
#privilege varchar(10) = 'Not wanted' OUTPUT -- OUT: 'admin' | 'user' | null
as
set nocount on
etc ...
My suggestion is to take the underlying SQL, tweak it if needed and use it in your view.

While doing additional research I found a much easier and different way of accomplishing my goal. I went with the Is_Member function. It accesses the permission path without having to work with SYSTEM_USER. I have done a few tests and it appears to do what I want.
CREATE VIEW [dbo].[vw_SomeTableName]
SELECT * FROM SomeTableName
where WHERE Is_Member( UserName ) = 1 )
The Is_Member function indicates whether the current user is a member of the specified Microsoft Windows group or SQL Server database role.
IS_MEMBER ( { 'group' | 'role' } )
Return value:
Current user is not a member of group or role, returns 0.
Current user is a member of group or role, returns 1.
Either group or role is not valid. When queried by a SQL Server login or a login using an application role, returns NULL for a Windows group.
For more on Is_Member check out msdn.microsoft.com on Is_Member or look at some other stack overflow questions.

Related

Stored procedure returned value

I've implemented Bcrypt on SQL Server. It's working fine.
I'm trying now to create a procedure where the user send his username and password (unhashed) and the procedure will return whether he is allowed to do so or not.
CREATE PROCEDURE CONNECTION
#Login_name VARCHAR(15),
#not_hashed_pass CHAR(15)
AS
BEGIN
DECLARE #is_match BIT
SET #is_match = (SELECT
dbo.CheckPassword(#not_hashed_pass,
(SELECT user_password FROM USERS
WHERE LOGIN_NAME= #Login_name)))
IF #is_match = 1
PRINT 'connected'
ELSE
PRINT 'not connected'
END
If I execute the procedure, I get the following result
Not connected (#is_match = 0)
If I try to execute the query that I'm setting to #is_match however:
SELECT
dbo.CheckPassword('azeqsd456789',
(SELECT user_password FROM USERS
WHERE LOGIN_NAME = 'Bob))
I get a 1 returned.
Both the query and procedure are executed with the same values.
Apart from using hashed passwords, your issue is a confusion of char() and varchar(). Your procedure is declared as:
CREATE PROCEDURE CONNECTION (
#Login_name varchar(15),
#not_hashed_pass char(15)
)
The password you are passing in has 12 characters, so it is being padded with three spaces to ''azeqsd456789 '. Hence, they don't match.
I would suggest fixing the procedure definition:
CREATE PROCEDURE CONNECTION (
#Login_name varchar(15),
#not_hashed_pass varchar(15)
)
But more importantly, I would strongly, strongly urge you to never use cleartext passwords and always hash them.

Is it possible to have a table/view return a scalar value when selecting from it?

I am running an SSRS report, and my data source is FetchXML.
Unfortunately, with FetchXML, you are unable to run stored procedures or functions.
This question can be asked several different ways:
How do I pass the value of a function into FetchXML?
Instead of doing the above, is it possible to just create a table or a view tbl_finduserGUID that will return the value of the function dbo.fn_finduserguid()?
Or Perhaps there's a better way to get the database role of the current user through SSRS / FetchXML?
Here's my query that will be converted to fetchxml:
SELECT systemuser.fullname AS 'fullname',
usersettings.calendartype AS 'calendartype',
usersettings.uilanguageid AS 'uilanguageid'
FROM filteredsystemuser AS systemuser
INNER JOIN filteredusersettings AS usersettings
ON systemuser.systemuserid = usersettings.systemuserid
WHERE systemuser.systemuserid = dbo.Fn_finduserguid()
Here's the source of dbo.fn_finduserguid()
SET ansi_nulls ON
go
SET quoted_identifier ON
go
ALTER FUNCTION [dbo].[Fn_finduserguid] ()
returns UNIQUEIDENTIFIER
AS
BEGIN
DECLARE #userGuid UNIQUEIDENTIFIER
--- test whether the query is runing by priviledged user with user role of CRMReaderRole
--- if it is dbo, we trust it as well.
--- There is an issue in SQL. If the user is a dbo, if it not member of any role
IF ( Is_member('CRMReaderRole') | Is_member('db_owner') ) = 1
BEGIN
SELECT #userGuid = Cast(Context_info() AS UNIQUEIDENTIFIER)
END
IF #userGuid IS NULL
BEGIN
SELECT #userGuid = s.systemuserid
FROM systemuserbase s
WHERE s.domainname = Suser_sname()
END
RETURN #userGuid
END
You should have no problem creating a view that select the function as a column.
Create view vFindUserGuid
As
Select dbo.Fn_finduserguid() as userguid
Your query can then use the view like a normal table
SELECT systemuser.fullname AS 'fullname',
usersettings.calendartype AS 'calendartype',
usersettings.uilanguageid AS 'uilanguageid'
FROM filteredsystemuser AS systemuser
INNER JOIN filteredusersettings AS usersettings
ON systemuser.systemuserid = usersettings.systemuserid
WHERE systemuser.systemuserid = (select userguid from vfinduserguid)

Setting a variable to the result of a select query SQL server 2012

I am trying to find the most economical way of achieving a way to return the data I want and also updating another table within the same Stored Procedure.
I have drastically simplified my SQL below to illustrate my issue.
Here's what I want to achieve :
DECLARE #UserID INT
SELECT TOP(1) #UserID = UserID, UserName, email, (#Loads of other columns#) FROM Users
UPDATE Logins SET LoggedIn = 1 WHERE UserID = #UserID
I understand I could do this by making sure that all returned columns are assigned to a local variable, but there are too many to be an efficient SPROC.
I don't want to have to do the SELECT statement twice (once to return the data and once to set the variable, ready for the update statement)
Any suggestions guys ?
Thanks,
Scott
You could use OUTPUT to get values to a local table variable but you still have to use a local SELECT to get a single value from the table variable.
DECLARE #TBL TABLE(userid int, username varchar(50), email varchar(50), logged bit)
DECLARE #userid int
UPDATE TOP (1) Users
SET logged = 1
OUTPUT deleted.* INTO #TBL
SELECT top (1) #userid = userid from #TBL
SELECT #userid
Fiddle Example

Insert data using Store Procedures

Let's say after I had login I will be prompt to enter the Name and address how do I insert data with at least 2 table linking to each other using Store procedures?
Try something like this:
CREATE PROC dbo.user_Add(
#username VARCHAR(50),
#password NVARCHAR(50)
)
AS
BEGIN
-- username should be unique
IF NOT EXISTS(SELECT TOP 1 1 FROM Login WHERE username = #username) BEGIN
INSERT INTO Login (username, password)
VALUES (#username, #password)
RETURN ##IDENTITY
END
ELSE BEGIN
-- already taken username should be handled somehow
RETURN 0
END
END
CREATE PROC dbo.user_StudentAdd(
#loginID INT,
#name NVARCHAR(50),
#addr NVARCHAR(100)
)
AS
BEGIN
-- add new record for new student
-- probably before you should check if student is not already exist
INSERT INTO Student (LoginID, [Student Name], [Student address])
VALUES (#loginID, #name, #addr)
-- returns new StudentID, you can also use `RETURN` to return StudentId (if you need it)
SELECT ##IDENTITY
END
As your homework you can write a third stored procedure which calls two above
and use result from first one in second one. Hint -- use EXEC to assign value returned by
stored procedure (here are details: http://msdn.microsoft.com/en-us/library/ms174998.aspx).
I didn't test it, so it can fails due to misspelling.
Also I've never used columns with space inside so I am not sure how to handle them.
I assume that:
User (record in login table) already exists and value of login.loginID is accessible (user has already logged-in).
It was checked that logged-in user needs to fulfill his students data.
One more thing -- I would not use column names with space (or non-Latin characters).

Changes to sysusers and sysxlogins in SQL 2008

I am currently updating a MS SQL 2000 server to SQL 2008. One of the issues highlighted by the Upgrade advisor is that the undocumented table sysxlogins has been removed.
I currently have a procedure that is run by a user 'foo' to determine if the user 'bar' exists in the database blah. If the user exists the user's password is compared to the password that was passed in to the procedure in order to determine if bar is allowed to log in to an application, it looks like this:
#UserName Varchar(50),
#Password Varchar(50)
As
Set NoCount On
------------------------------------------------------------------------------------
-- Check username
------------------------------------------------------------------------------------
If Exists
(
select top 1 name
from blah.dbo.sysusers With (NoLock)
where name = #UserName
)
Begin
------------------------------------------------------------------------------------
-- Check Password
------------------------------------------------------------------------------------
If Not Exists
(
Select *
From master.dbo.sysxlogins With (NoLock)
Where srvid IS NULL
And name = #Username
And ( ((#Password is null) or (#Password = '') and password is null)
Or (pwdcompare(#Password, password, (CASE WHEN xstatus&2048 = 2048 THEN 1 ELSE 0 END)) = 1))
)
Begin
Return 2
End
Else
Begin
------------------------------------------------------------------------------------
-- Check Role
------------------------------------------------------------------------------------
Select usg.name
From blah.dbo.sysusers usu
left outer join (blah.dbo.sysmembers mem inner join blah.dbo.sysusers usg on mem.groupuid = usg.uid) on usu.uid = mem.memberuid
left outer join syslogins lo on usu.sid = lo.sid
where usu.name = #Username
and usg.name not like 'db_%'
Return 0 -- Username and password correct
End
End
Else
Begin
Return 1 -- Username incorrect
End
This all works fine under SQL 2000, yet I must now pay the price of using undocumented system tables and make it work under 2008.
There are two problems with this, the first problem is that foo can no longer see all of the database users when executing:
select * from blah.dbo.sysusers
or Microsoft's recommended alternative:
select * from blah.sys.database_principals
I understand that this is due to the fact that members of the public role no longer have access to object meta data unless they are a member of sysadmin or have the View Definition permission on the object.
It is not possible for foo to be a member of sysadmin, so as far as I understand I need to grant foo the View Definition permission, but on which object? I don't think I do it on the system view, so do I do it on every single user?
Secondly, and similarly, I need to change my reference to sysxlogins to sys.sql_logins. Again foo can only see itself and sa when executing
select * from sys.sql_logins
How can I get foo to see all of the server logins in this list?
There will no doubt be similar problems when accessing sysmembers and syslogins later on in the code but hopefully an understanding of the two examples above will help me to sort the rest out.
Thanks in advance,
You can grant the SELECT right directly on sys.database_principals, as long as the login has a user in the master database. For example:
use master
create user MyUser for login MyUser
grant select on sys.database_principals to MyUser
Then, in SQL Server 2008, passwords are encrypted, even for the administrator. You can, however, verify a password by trying to change it. The change procedure will give an error if the old password is incorrect.
declare #rc int
begin try
exec #rc = sp_password 'welcome', 'welcome', 'MyUser'
end try
begin catch
set #rc = ERROR_NUMBER()
end catch
-- Should be 0 on success
select #rc
For this to work, you have to disable Enforce password policy in the Login Properties dialog. Otherwise, the policy would prevent you from changing your password too often.
I think GRANT SELECT ON... is more troublesome as one have to add the user to the master database. The below was the solution for me:
USE master
GRANT VIEW ANY DEFINITION TO foo
If you have an app that works on various versions of SQL you need to check if the server version is higher then 8 (GRANT VIEW ANY DEFINITION works from SQL 2005 though it seemes not be needed there).