ASP.NET loses user context when connecting to database - c#

In ASP.NET, we have an application that prompts users for their AD credentials using Basic Authentication and ASP.NET Impersonation.
The application then connects to SQL Server with the following connection string
SqlClient.SqlConnection cnn = new SqlClient.SqlConnection();
cnn.ConnectionString =
"Server=" + MenuServer + ";" +
"Database= " + MenuDatabase + ";" +
"Trusted_Connection=Yes;" +
"Pooling = False;";
cnn.Open();
99% of the time, this passes along the user context just fine, but sporadically we get the following error from SQL Server:
Login failed for user 'WebServerName$'. Reason: Could not find a login matching the name provided
Meaning that it has been unable to pass along the user credentials and instead defaulted to the IIS worker process. Interestingly, when we catch the error, the current user will still be accurately recorded with the following code:
string userName = Environment.UserName;
Questions:
What user context does the Integrated Security pass along to the database?
Is it possible to programmatically check the user before calling cnn.Open() to confirm we have a real user?

This can happen if you are using ORM Mappers, e.g. Entity Framework or NHibernate (I have experuienced this with NHibernate). It is because the mapping framework uses Lazy Evaluation: it doesn't get all the data from the database until you ask for it. For example, if you are getting a list of objects each of which contains a further list of objects (e.g. Customer contains a list of Invoice), it won't get the Invoice objects until you actually use them.
If you don't use then until binding them to an aspx control and you do that in the aspx page (not in e.g. the PageLoad event), then the life-cycle of an aspx page means that it looks at the sub-list after your code has run and any impersonation you are doing has ended - this under the IIS account.
You can prevent this happening by either preventing lazy evaluation (generally not a good idea), or by making sure that you touch each of the lists you'll need the page to use in your code, under your impersonated account.
for each (Customer c in customers)
{
int i = c.Invoices.Count; // make sure that they do get retrieved.
}

Related

How to loop through multiple connection strings in a db and query them?

I don't know how to loop through my local database and add the values to this string:
string credentials = #"server = 127.0.0.1;
user id = system;
port = 3308;
Password = 975315";
https://i.imgur.com/6B4YFw7.png
I kind of need something like this:
string credentials = #"server = IDindb;
user id = IDindb;
port = Portindb;
Password = Pwindb";
foreach(rowofdata in localdb)
{
MySqlConnection con = new MySqlConnection(credentials);
con.Open(); //Opens the connection
MySqlCommand cmd = new MySqlCommand(sqlQuery, con); //Sends the
query to "show slave status"
Create a new table in browser that shows if server is running or not.
}
This is quite a good homework assignment - It touches frontend, database, networking and middleware, and importantly it forces you to properly think through all the little decisions of what might go wrong, e.g.
what do you do when the server doesn't respond (write "server down
into your html table)
how do you trap this without your own code failing.
What if the servers take a really long time to respond? Timeout?
Should you be doing this to each server in succession, or
all at once, adding their responses into the table as you go?
What I'm saying is you should think about your overall solution some more before diving into the code.
To answer the question, assuming you have some code to connect to the local db and query the table, rowofdata will be a recordset, with each row populated per the row in your image.
Extract the server, user, password etc credentials from it:
var masterHost = rowofdata["Master_Host"].ToString();
and create the connection string inside the for-loop.
var credentials = $"server={masterHost};user id={userId}; port={portId}; Password={password}";
The Create new Table in browser part will depend on which platform/language you're writing in. And in any case shouldn't be done here, unless you're intent on mixing db access with html (#Razor? classic asp?).
Store the server / status values in a list (of server object, with status etc), then subsequently use them to populate the html.
(Or build the html table and render that, and use ajax to request the status of each).
(unrelated - how do you plan on implementing the stop/start/fix button code?)

How do you connect from ODP.NET to Oracle (12G+) by proxy user with no password

There seems to be no answer online as to how you can use Oracle Data Provider for .NET (ODP.NET) to connect to Oracle (12G and later) in a very specific scenario:
User is identified externally on a database
User is granted access to another schema (application user) by proxy connect
User has been set up like this:
CREATE USER user_in_question
IDENTIFIED EXTERNALLY
-- etc.
And connect by proxy has been set up like this:
ALTER USER specified_app_user GRANT CONNECT THROUGH user_in_question
The logical approach when creating the ODP.NET OracleConnection string would be something like this (using the user friendly OracleConnectionStringBuilder):
var connBuilder = new OracleConnectionStringBuilder
{
UserID = "/", // External login using the user running the program
ProxyUserId = "specified_app_user",
DataSource = "database",
};
This does not work. Nor does providing blank "Password" or blank "Proxy Password". Nor does removing the UserId.
So how do you connect using ODP.NET in these circumstances?
The answer (which I spend an hour searching for without any luck) is actually really simple, yet not very user friendly:
var connBuilder = new OracleConnectionStringBuilder
{
UserID = "[specified_app_user]",
DataSource = "database",
};
//connBuilder.ToString() output:
//"USER ID=[specified_app_user];DATA SOURCE=database"
This works in .NET 4.5+ on Oracle 12G+, but probably also on earlier platforms of .NET/Oracle/ODP.NET. I did not test it in ASP.NET, but it should work there too.
This way the UserId actually functions just like the ProxyUserId, just enclosed within brackets, just as you would normally log in on the Oracle Database using, say, Toad or SQlPlus.
It might also be possible using this format (but in my case the connection string had to be compatible with the OraOLEDB format so that did not work):
//Without the use of the conn string builder class, just for the fun of it...
var connString = "User Id=specified_app_user;Data Source=database;Proxy User Id=/";
EDITED 2nd March 2017: The line above does not seem to work in certain cases. Added comment about it and here is the code that IS working:
USER ID=[specified_app_user];DATA SOURCE=database
This info does not seem to exist anywhere - else I overlooked it, and in that case PLEASE do correct me.

Get windows user logged C# ASP

I try with a lot of options to get the current user logged in Windows.
I have the network user with this.
HttpContext.Current.User.Identity.Name
I haven't user login forms and I dont want put this in my proyect because I dont need this.
With
Environment.UserName
I have this ="ASP.NET V4.0 Integrated"
I'm trying to get the current user with this code, but I have an object with 4 users, 2 Administrator, an invited user and my current user, but I dont find something to difference between this.
SelectQuery sQwuery = new SelectQuery("Win32_UserAccount", "Domain='" + System.Environment.MachineName + "'");
ManagementObjectSearcher mSearcher = new ManagementObjectSearcher(sQwuery);
foreach (ManagementObject mObject in mSearcher.Get())
{
var users= mObject;
}
Those are my 4 users
base = {\\DOOKU\root\cimv2:Win32_UserAccount.Domain="DOOKU",Name="Administrador"}
+ [1] {\\DOOKU\root\cimv2:Win32_UserAccount.Domain="DOOKU",Name="Administrator"} object {System.Management.ManagementObject}
+ [2] {\\DOOKU\root\cimv2:Win32_UserAccount.Domain="DOOKU",Name="Invitado"} object {System.Management.ManagementObject}
+ [3] {\\DOOKU\root\cimv2:Win32_UserAccount.Domain="DOOKU",Name="MyUser"} object {System.Management.ManagementObject}
I this case I need the last
There is other option to find this?
thanks.
With 'Environment.UserName', you are retrieving the user identity associated with the IIS Application Pool that hosts the IIS application. You want the identity of the authenticated user currently running the site.
If you have Anonymous Access disabled and Windows Authentication enabled on the site, you really should leverage
HttpContext.Current.User.Identity.Name
to get the name associated with the user running your application. I note in your original post that you 'don't need that,' but for what I'm understanding to be your requirements, I think you do. The current HTTP context holds the user identity.

Force local user to change password at next login with C#

I'm writing a function for a web app in ASP.NET where the client logs into the server machine, which is Windows authenticated against the local users on the server. The function I am writing resets the users password and emails them the new one. I do this like so:
String userPath = "WinNT://" + Environment.MachineName + "/" + username.Text;
DirectoryEntry de = new DirectoryEntry(userPath);
de.Invoke("SetPassword", new object[] { password });
How can I also check the flag to force the user to change their password the next time they log in with the password emailed to them? I tried using pwdLastSet like so:
de.Properties["pwdLastSet"].Value = 0;
But this apparently only works with LDAP, not WinNT, and I am doing this locally.
Any experts know any better than me? I have even tried looking for a way to do this through the command line so that I can just create a Process, but I haven't been able to find a way to do it that way, either.
For WinNT, you must set the value to 1 rather than 0, and the property name is "PasswordExpired" rather than "pwdLastSet"; see http://msdn.microsoft.com/en-us/library/aa746542(VS.85).aspx
In other words, do this for WinNT:
de.Properties["PasswordExpired"].Value = 1;
(It is confusing, I know, but for LDAP you need to set the property "pwdLastSet" to 0. How's that for inconsistency!)

Using SMO.Agent to retrieve SQL job execution status - security issue

I've got a C# program that fires off SQL Server Agent jobs using the SQL Server Management Objects (SMO) interfaces. It looks something like:
Server ssis_server = new Server(
new ServerConnection(SERVER_NAME, SERVER_USERNAME, SERVER_PASSWORD)
);
var agent = ssis_server.JobServer;
var ssis_job = agent.Jobs[job_name];
var current_status = ssis_job.CurrentRunStatus;
if (current_status == JobExecutionStatus.Idle)
{
ssis_job.Start();
OnSuccess("Job started: " + job_name);
}
else
{
OnError("Job is already running or is not ready.");
}
I'm using SQL Server Authentication at this point to simplfy things whilst I work this out.
Now, my problem is that unless the SERVER_USERNAME is part of the 'sysadmin' dbo role, ssis_job.CurrentRunStatus is always 'Idle' - even when I know the job is running. It doesn't error out, just always reports idle.
If the user is an administrator, then the status is returned as expected.
Role membership you say?
Well, I added the SERVER_USERNAME SQL Server login to the msdb Role SQLAgentOperatorRole, that didn't seem to help.
The job's owner is a system administrator account - if that's the issue I'm not sure how to work around it.
Any ideas?
You need to refresh the job by calling the Refresh() method on ssis_job before checking the status, then you will get the correct information.

Categories