Create logins, create user from stored procedure in Azure - sql-server

I've inherited a very old app that runs on SQL Server 2005. I'm trying to move this app to Azure.
One thing the stored procedures in this app do is create new database logins and new users. I know that many of the functions and system procedures it uses no longer exist in SQL Server, or in Azure.
Is there some equivalent of procedures like sp_addlogin, sp_droplogin, sp_grantdbaccess, sp_revokedbaccess etc. which can be executed from a user database (the procedures are there and must run there)?
I see lots of articles that talk about "connect to master", but how do I do that from within a stored procedure on my custom database?
For example, the "create login" command has to be run in master, but my procedures are in a user database. How do I do that?
Any help would be appreciated. If what I'm trying to do isn't possible, I'd appreciate knowing that, too.

I'm not sure where you get the idea that CREATE LOGIN has to be run in the master database. Seems to work just fine when run from a user database:
use master
go
drop database if exists StackOverflow;
create database StackOverflow;
go
use StackOverflow;
go
if exists(select * from sys.syslogins where name=N'StackOverflow')
drop login [StackOverflow];
create login StackOverflow
with password='St4ck0verflow',
default_database = StackOverflow;
create user StackOverflow
from login StackOverflow
with default_schema = dbo;

Related

How to create a login that ONLY has access to run stored procedures?

I have a C# Winform application that interacts with an SQL Server DB via stored procedures. In SSMS, how do I create a login that ONLY has permissions to run stored procedures? That login wouldn't be able to view/edit/create table definitions, etc. It would also only have access to a single specified DB.
The reason I want to create such a login is because I store the SQL Server credentials used by my Winform application in its App.config file. Since the app.config can easily be read, anyone with malicious intent can easily perform unwanted operations on the database if the given login had any other permissions than just stored procedures.
A neat trick in this scenario is to create a separate (custom) SQL Server role that can only execute stored procedures:
CREATE ROLE db_executor;
GRANT EXECUTE TO db_executor;
This role now has the permission to execute any stored procedure in the database in which it's been created - and in addition: that permission will also extend to any future stored procedures you might create later on in this database.
Now create a user in your database and give it only this database role - this user will only be able to execute stored procedures - any and all of them in your database.
If you user should be allowed to execute any and all stored procedures - this is a very convenient way to allow this (and you don't have to constantly update the permissions when new stored procedures are created).
You can use the following query in order to allow stored procedure execute permision to your user
USE [DB]
GRANT EXECUTE ON dbo.procname TO username;
However, in my humble opinion , you should secure the connection string in the app.config.
Maybe , this How to store login details securely in the application config file link can be helped to you.
The access to a specific database is done through creating a user on the database that you want him to operate on. You can find more infos about users here.
If the user is created you can Grant, With Grant and Deny actions for every single item on the database.
The user will then be granted/denied those rights by a grantor, which is the dbo by default.
You can use this to also deny him access to every item on your database that isn't your stored procedure, which is what you're looking for if I understand you correctly.
Try folloiwng approach (grant execute should be repeated for every SP). Note that MyStoredProcedure has to be in MyDatabase :)
-- create login to server
create login test_user with password = 'test';
-- create user mapped to a database
use MyDatabase
go
create user test_user for login test_user;
-- grant permission to execute SP
grant execute on MyStoredProcedure to test_user

Sql Server Agent job failing to execute stored procedure

I have a stored procedure that I can execute in SSMS with a non domain SQL Server user.
This stored procedure selects data from tables in one database (DB1) truncates and selects into a table in DB2.
The user has datareader,datawriter and dbowner for both databases.
Problem:
When I execute the stored procedure via SS Agent with execute as the user I get the following error
The server principal [user] is not able to access the database [DB1]
under the current security context.
Actions taken So far:
I have tried to resolve this so far by:
Turning on db chaining for both databases
Deleted the user from DB1 and added again
Checked using EXEC sp_change_users_login #Action=’Report’ to see if user orphaned. As this is a database that is a restore of a live one. However I added the user after the restore. The user was not listed as orphaned
A possible workaround if you don't want to have the owner be sa is to have the user be a member of msdb and grant the the SQLAgentOperatorRole in msdb. See if that works.
But to be honest, either use sa or a dedicated service account with enough permissions. It's better if the job runs under that context.

Error that SQL Login already exists

I have created 2 SQL Server Database Projects in VS 2013 and imported schemas from 2 databases that reside on the same SQL instance. Both databases have SQL Users, let's say [MyUser1] and [MyUser2], that use a single SQL Login, let's say [MyLogin]. The issue is both projects want to create the SQL Login and this causes the error
"SQL71508 The model already has an element that has the same name MyLogin"
DB1 Project
CREATE LOGIN [MyLogin] WITH PASSWORD = N'xyz';
CREATE USER [MyUser1] FOR LOGIN [MyLogin];
DB2 Project
CREATE LOGIN [MyLogin] WITH PASSWORD = N'xyz';
CREATE USER [MyUser2] FOR LOGIN [MyLogin];
I've tried:
Removing CREATE LOGIN from one of the projects. Issue is then CREATE USER generates an error because it wants the CREATE LOGIN for the Login to exist.
To find a way to ignore the error, but all I've found is how to ignore warnings.
Using the following to check if Login already exists, however an error is generated at the "If" indicating, "SQL700001 The statement is not recognized in this context".
If Not Exists (Select name From master.sys.server_principals Where name = 'MyLogin')
Begin
CREATE LOGIN [MyLogin]
WITH PASSWORD = N'xyz';
End
Does anyone have any thoughts on how to correct or work around this issue? Thanks!
Also, my primary reason for creating the DB2 project was due to views in DB1 that are based on tables in DB2. Without the DB2 project referenced in the DB1 project, the SQL to create the views generated errors about not finding the tables in DB2. So, if anyone has any thoughts on how to workaround this issue, that would be helpful too.
Never add the same item twice in SSDT projects. Where the same item exists in more than one project, create a master project for the server.
Leave the users alone, just move the logins to a common project.
Think inheritance when designing SSDT projects. Do not forget to include the IncludeCompositeObjects switch for deployments of each individual database.

SQL server Login and User

I am confused with Login and User. I found following in articles :
A "Login" grants the principal entry into the SERVER.
A "User" grants a login entry into a single DATABASE.
One "Login" can be associated with many users (one per database).
I can understand it theoretically. But, I think I might not have understood this practically.
I created a Login in my SQL server 2008 management studio by right clicking SERVERNAME=>SECURITY=>LOGIN. The default database was "master". Now, I can log into sql server with this login name and password. I noticed that if I change the default database in Login properties to a specific database, I cannot login again with this credential. I reverted back to "master" and it works. What happened here?
Also, why do we need users? I created a user by right clicking DATABASENAME=>SECURITY=>USERS. I cannot relogin with this user credential. So, what is the purpose we need this for. I can understand the theory of this answer but I need little more explanations to make sense.
Also, I am a .net developer, so I would like to know, what are the credentials provided in sql connection strings. Are they login or user or can be any of these?
The simplest explanation is that the SQL Server login gets you into the server, and the settings on that login control how it works in each database.
Don't worry about the database logins for the moment. You already went to SERVERNAME=>SECURITY=>LOGIN. Let's look at what you do with this login - right click and go into properties if you already created the login. Look under Roles - there are a number of different ones with different purposes on the server. But for an app, generally speaking, the average user should only have the Public role.
As far as the Database login, you go to the Mapping section to point your login to whichever databases it needs access to. When you map the login to the database, this CREATES the database login you saw under DATABASENAME=>SECURITY=>USERS if it does not already exist. The mapping is the most important part though, it's what literally gives the login the ability to see data in the database.
For applications, you're using the server login. If you set up the links to the databases you need in mapping, you don't really need to think about the database level login info.
The login only exists at the server level, which is why it maps automatically to the master database.
Users control access to individual databases. When you create a user, you can map it to a login (see Create User on MSDN for the syntax). If you create a user mapped to the login in the database, you can set it as the default and log in.
One reason this is done is to allow multi-tenant environments where a single server hosts many databases that not everyone who can access the server should be able to access. For example, say we provide services to Company A and Company B, and we host the database for each on the same server. We don't want someone from Company A (or, more importantly, someone who has compromised the credentials of someone from Company A) to be able to access the data for Company B, so we only create a user for the Company A login in the Company A database. Here's a brief code setup for you to experiment with:
-- This script assumes whoever is running it has sysadmin permissions on the instance of
-- SQL Server on which it is running. Do not run this on a production instance.
-- Create a database for each company on the server instance.
create database CompanyA;
create database CompanyB;
go
-- Create a login for each company on the server instance.
-- SQL Server integrated security has it's issues, but it's useful for an example like this.
create login CompanyA_Login with password = 'pa55wOrd1', default_database = CompanyA;
create login CompanyB_Login with password = 'pa55wOrd2', default_database = CompanyB;
go
-- Create a user in the appropriate database for each login.
-- We need to tell the server that we want to use a specific database
use CompanyA;
create user CompanyA_User for login CompanyA_Login;
-- We're granting it dbo for the purposes of our example here;
-- a broad permission set like that is a bad practice.
alter role db_owner add member CompanyA_User;
go
-- Repeat the process...
use CompanyB;
create user CompanyB_User for login CompanyB_Login;
alter role db_owner add member CompanyB_User;
go
-- Create a table in each database and populate it with some data.
use CompanyA;
create table dbo.sensitiveInformation
(
sensitiveInformation NVARCHAR(50) NOT NULL
);
insert dbo.sensitiveInformation (sensitiveInformation)
values ('Oh man, it would be bad if this got out!');
go
use CompanyB;
create table dbo.sensitiveInformation
(
sensitiveInformation NVARCHAR(50) NOT NULL
);
insert dbo.sensitiveInformation (sensitiveInformation)
values ('Oh man, it would be even worse if THIS got out!');
go
-- Now, feel free to log in as either user and see what you can and can't do.
-- You will find that the CompanyA_Login will never be able to access CompanyB's
-- data and vice versa. This allows for secure multi-tenant environments.
-- Once you're done playing around, we'll clean up our samples.
use CompanyB;
drop table dbo.sensitiveInformation;
drop user CompanyB_User;
go
use CompanyA;
drop table dbo.sensitiveInformation;
drop user CompanyA_User;
go
use master;
drop login CompanyB_Login;
drop login CompanyA_Login;
drop database CompanyB;
drop database CompanyA;
If you need separate, discrete security/permission sets, and you need one user to have more than one of these sets, you want to use database roles. This article from Tech Republic gives a decent gloss of the benefits of roles, though I'd recommend checking MSDN for the most current ways of creating them.

How to disable SQL Server Management Studio for a user

Is there a way to prevent users from getting into SQL Server Management Studio so that they can't just edit table rows manually? They still need to access the tables by running my application.
You can use the DENY VIEW ANY DATABASE command for the particular user(s). This is a new feature available in SQL Server 2008.
It prevents the user from seeing the system catalog (sys.databases, sys.sysdatabases, etc.) and therefore makes the DB invisible to them in SQL Management Studio (SSMS).
Run this command from the Master Database:
DENY VIEW ANY DATABASE TO 'loginName'
The user is still able to access the database through your application. However, if they log in through SSMS, your database will not show up in the list of databases and if they open a query window, your database will not appear in the dropdown.
However, this is not fool-proof. If the user is smart enough to run the Query Command:
USE <YourDatabaseName>
Then they will see the database in the Query Analyzer.
Since this solution is taking you 90% there, I would give the database some obscure name not let the users know the name of the database.
You DO NOT need to worry about them having access to the tool. Simply make sure they do not know any of the SQL logins for the specific Databases that have read/write permissions, if they do, change the password. If they have access to the DB via Windows Authentication, make sure that they are in a datareader role. You can use roles to manage what the users can do in SQL.
You can use a trigger.
CREATE TRIGGER [TR_LOGON_APP]
ON ALL SERVER
FOR LOGON
AS
BEGIN
DECLARE #program_name nvarchar(128)
DECLARE #host_name nvarchar(128)
SELECT #program_name = program_name,
#host_name = host_name
FROM sys.dm_exec_sessions AS c
WHERE c.session_id = ##spid
IF ORIGINAL_LOGIN() IN('YOUR_APP_LOGIN_NAME')
AND #program_name LIKE '%Management%Studio%'
BEGIN
RAISERROR('This login is for application use only.',16,1)
ROLLBACK;
END
END;
https://www.sqlservercentral.com/Forums/1236514/How-to-prevent-user-login-to-SQL-Management-Studio-#bm1236562
I would suggest you lock down the database and give appropriate read-only (or other) rights to the user. That way the user can still use management studio to run select queries and such.
If you don't want the user to have any rights at all then you could do that as well.
If your application is running as a service/user account then only that account requires access to the database. The individual users' account do not require any access to the database and therefore they won't even have read access. Your app will be the gateway to the data.
If the users are running the application under their user accounts then grant them read-only permission. You can simply add them to the db_datareader role.
Hope this helps!
You can deny 'Users' access rights to the ssms.exe executable file, while granting the relevant users/administrators rights to it.
If your application only used stored procedures to modify the data, you could give the end users access to run the stored procs, but deny them access to modify the tables.
Don't let them know what the database login is.
If you can't restrict the login, use stored procedures exclusively for updates and disable any CREATE,DELETE,INSERT, or UPDATE permissions for that user.
An Application Role will allow you to secure database objects to your application instead of the logged on user.
I agree with Jon Erickson as a general rule
do not allow any users access to the tables, but only allow access through stored procs
do not allow general user accounts access to stored procs, but only the account your app runs under (whether it's an integrated login or SQL login)
Make well usage of Database Roles, if Users should only have SELECT (read) access assign them the db_datareader Role. Even if they login using SSMS they will can execute only SELECT statements.

Resources