Adding a Database User for each Customer - sql-server

I'm creating a database that has two main parts: a front end which the customer sees and a backend that only the company can see.
It is an email subscriber system so I want the customer to be able to view, add, edit and delete their entries in the email subscriber table, but all the admin-related tables should be hidden from them.
As I'm fairly new to SQL Server 2017, I'm still getting my head around logins, users and roles.
The main question I wanted to ask is what is the best way of doing this? I know I can manually set up database users and give them grant/deny permissions, but how do I do this automatically so that every time a customer's details are added to a new row in the customer table, a new database user in the sidebar is added.

Try this
public static void AddUsersToDatabase(string databaseserver, string databasename, string usertobeadded)
{
using (SqlConnection conn = new SqlConnection("server=" + databaseserver + "; database=" + databasename + "; User ID=WPDOMAIN\\spdev; Integrated Security=SSPI; password=Password123;"))
{
conn.Open();
string password = "Password123";
string sql = "CREATE LOGIN " + usertobeadded + " WITH PASSWORD = '" +
password + "'; USE " + databasename + "; CREATE USER " + usertobeadded + " FOR LOGIN " + usertobeadded + ";";
SqlCommand cmd = new SqlCommand(sql);
cmd.ExecuteNonQuery();
conn.Close();
}
}

Related

How to get the current user of the system and store to database?

Why the value stored in the database is the username of the SQL Server instead of the username of the system user? How to get the name of current system user?
string Query = "insert into Locate(Locate_LongLatOut, Locate_IC, CreateDate, CreateBy) values('" + this.txtCoordinate.Text + "', '" + this.txtCustomerIC.Text + "', getdate(), USER_NAME()); ";
Use CURRENT_USER instead USER_NAME().
i.e:
SELECT CURRENT_USER;

data always getting from the previous databse not from the new database

I am facing an issue from sql server that
SqlDataAdapter sda = new SqlDataAdapter(
"Select Role from Login where username ='" + textBox1.Text + "' and password '" + textBox2.Text + "' ",
con);
My sql command always get data from the pervious database not from the new one.
Your connection(con) is pointing to your old database change it to your new database.

How to handle data duplication in c#

While clicking on add button data is saved in database but after 2-3 times refresh data in database there 2-4 copies of same data is shown.
How to get to fix this?
String cs = ConfigurationManager.ConnectionStrings["MyDBConnectionString1"].ConnectionString;
using (SqlConnection con = new SqlConnection(cs))
{
SqlCommand cmd = new SqlCommand("Insert into tblBrands values('" + txtBrandName.Text + "')", con);
con.Open();
cmd.ExecuteNonQuery();
txtBrandName.Text = string.Empty;
}
If you are trying to solve in SQL (assuming from your tags) you could check before inserting using:
if not exists (select * from tblBrands where ...)
Build your where clause based on your criteria - what would you consider duplicate entry
More info on exists in Microsoft Docs

How to create a Database user in Entity Framework

Been working on my first Entity Framework project. As part of the project I am going to be creating a number of SSRS reports. In order to connect to the database I need to have a Reports user that will only access to the specific database on the server. In the past i have always written a script to add Database users but I want to know is there a way that i can do this using Entity Framework instead?
Assuming your user already has a login defined at the SQL Server level (Security > Logins), you can call the following method from your DB initializer seed method to add the user to the database:
private void AddDbUser(MyDataContext myDB)
{
string accountDomainName = "AccountDomainName"; // replace with user's login domain
string accountLoginID = "AccountLoginID"; // replace with user's login ID
string sql =
"USE [MyDB]" +
"CREATE USER [MyNewUser] FOR LOGIN [" + accountDomainName + "\\" + accountLoginID + "]" +
"ALTER AUTHORIZATION ON SCHEMA::[db_datareader] TO [" + accountLoginID + "]" +
"ALTER AUTHORIZATION ON SCHEMA::[db_datawriter] TO [" + accountLoginID + "]" +
"EXEC sp_addrolemember N'db_datawriter', N'" + accountLoginID + "'" +
"EXEC sp_addrolemember N'db_datareader', N'" + accountLoginID + "'";
myDB.Database.ExecuteSqlCommand(sql);
}
The exact SQL needed is dependent on your configuration. To have SQL Server generate the SQL for your scenario, you could open the add user dialog in SSMS (Database > Users > New User...), fill out the fields, and click the "Script" button at the top instead of hitting OK at the bottom. Note that any "GO" lines will need to be removed from the generated script before pasting it into the method above.
You would need to tell EF to use the appropriate stored procedures to do so. You could also wrap these up in a sproc of your own that wraps the relevant commands. There is no native "CreateReportsUser" type method within EF that I know of.
Edit: I probably should have provided this reference to be a "complete" answer. Apologies.
Here's how you can do what I recommend: How to call Stored Procedure in Entity Framework 6 (Code-First)?

EF, Code First, how to initialize database on a specific location (HDD)

does anybody know how to spedify exact .mdf/.log file location during EF CodeFirst database initialization?
For a debug and testing scenario (but specifying exact DB files location can be useful in production as well), I'd like to re-deploy database during application start-up, but also I'd like to create DB files on a specific location (e.g. secong hard drive).
public class MyCustomInitialized : DropCreateDatabaseAlways<MyContext>
{
public override void InitializeDatabase(MyContext context)
{
if (context.Database.Exists())
{
// set the database to SINGLE_USER so it can be dropped
context.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, "ALTER DATABASE [" + context.Database.Connection.Database + "] SET SINGLE_USER WITH ROLLBACK IMMEDIATE");
// drop the database
context.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, "USE master DROP DATABASE [" + context.Database.Connection.Database + "]");
// Doesn't work yet
var directoryPath = ConfigurationManager.AppSettings["DatabaseDirectoryPath"];
var fileName = ConfigurationManager.AppSettings["DatabaseFileName"];
if(!Database.Exists()) {
var qry = string.Format(
"USE master CREATE DATABASE {0}" + Environment.NewLine +
"ON PRIMARY" + Environment.NewLine +
"( NAME = {0}, FILENAME = '{1}')" + Environment.NewLine +
"LOG ON" + Environment.NewLine +
"( NAME = {2}, FILENAME = '{3}')" + Environment.NewLine
, this.Database.Connection.Database,
Path.Combine(directoryPath, fileName + ".mdf"),
this.Database.Connection.Database + "_log",
Path.Combine(directoryPath, fileName + "_log.ldf"));
// -- EXCEPTION HERE,
Database.ExecuteSqlCommand(qry);
Database.ExecuteSqlCommand("USE " + this.Database.Connection.Database);
}
base.InitializeDatabase(context);
}
}
The exeption I get:
Cannot open database "DatabaseName" requested by the login. The login
failed. Login failed for user 'SomeUser'. A severe error occurred on
the current command. The results, if any, should be discarded.
If the database can be created by EF during initialization, how this initialization can be overriden?
Thanks,
Tom
I was able to get the below working, in both the database exists and non-exists scenarios, however, there is one issue in that the Code First table creation isn't being triggered (i.e. this is only a partial solution - there is still a step needed to actually get EF to create the Code First tables).
(My context was called Model1)
public class MyCustomInitialized : DropCreateDatabaseAlways<Model1>
{
public override void InitializeDatabase(Model1 context)
{
if (context.Database.Exists())
{
// set the database to SINGLE_USER so it can be dropped
context.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction,
"ALTER DATABASE [" + context.Database.Connection.Database +
"] SET SINGLE_USER WITH ROLLBACK IMMEDIATE");
// drop the database
context.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction,
"USE master DROP DATABASE [" + context.Database.Connection.Database + "]");
}
var directoryPath = #"c:\temp";
var fileName = "Foo";
var qry = string.Format(
"USE master CREATE DATABASE {0}" + Environment.NewLine +
"ON PRIMARY" + Environment.NewLine +
"( NAME = {0}, FILENAME = '{1}')" + Environment.NewLine +
"LOG ON" + Environment.NewLine +
"( NAME = {2}, FILENAME = '{3}')" + Environment.NewLine
, context.Database.Connection.Database,
Path.Combine(directoryPath, fileName + ".mdf"),
context.Database.Connection.Database + "_log",
Path.Combine(directoryPath, fileName + "_log.ldf"));
using (var conn = new SqlConnection(#"data source=.\SQLExpress;initial catalog=master;integrated security=True;"))
using (var cmd = new SqlCommand(qry, conn))
{
conn.Open();
cmd.ExecuteNonQuery();
}
-- TODO trigger the table creation
this.Seed(context);
context.SaveChanges();
}
}
Some notes:
Database belongs to the context
You won't be able to recreate the database on the Context connection, since when you drop the database, the Initial Catalog in your EF connection string will have disappeared. I've just used a new Ado Connection to do the Drop, else "Cannot open database \"FOO\" requested by the login". I would also consider moving the DROP of the Context connection into a master connection.
Calling
base.InitializeDatabase(context);
Causes the Db to be dropped and recreated because of the DropCreateDatabaseAlways strategy which includes:
context.Database.Delete();
context.Database.Create(DatabaseExistenceState.DoesNotExist);
So hence
-- missing something here, viz creating the tables
this.Seed(context);
context.SaveChanges();

Resources