How to get userID for username in ASMX webservice - c#

I'm trying to store a user's post into an access database through a web method. I want to store the logged-in user's username, user's post, and the post datetime.
So far, I can store an existing user post by hard coding. But I want to store posts by any logged-in users. I was told I need to get userID for username.
Thus, I've found and tried adding the following codes:
//GetUser() returns current user information
MembershipUser user = Membership.GetUser();
//Returns the UserID and converts to a string
string UserID = user.ProviderUserKey.ToString();
When I tried debugging with breakpoints, the first one was okay. But for the second one, VS 2010 said that "object reference not set to an instance of an object." How do I fix it?
VS suggested adding "new," which didn't work. It also suggested to catch NullReferenceException, but I don't know how to use the codes they provided:
public class EHClass
{
void ReadFile(int index)
{
// To run this code, substitute a valid path from your local machine
string path = #"UsersDB_in_App_Data";
System.IO.StreamReader file = new System.IO.StreamReader(path);
char[] buffer = new char[10];
try
{
file.ReadBlock(buffer, index, buffer.Length);
}
catch (System.IO.IOException e)
{
Console.WriteLine("Error reading from {0}. Message = {1}", path, e.Message);
}
finally
{
if (file != null)
{
file.Close();
}
}
// Do something with buffer...
}
}
Can you give me suggestions of what I need to do, or an alternative way to go about getting userID for username?

You didn't indicate what type of MembershipUser you have, but the ProviderUserKey is totally dependent on the underlying data store.
For example, the sql membership provider stores this value as a GUID.
If there is a possibility that this property won't contain any useful data, then you need to test it for existence before accessing it:
//Returns the UserID and converts to a string
string UserID;
if ((myObject != null) && (myObject.ProviderUserKey != null)) {
UserId = myObject.ProviderUserKey.ToString();
} else {
UserId = String.Empty;
}
In addition, unless you are using WSE in a straight asmx web service, I don't think that the memebership provider will have any valid data to operate on.
If this is the case, you will probably need to switch to WCF or implement WSE (NOT recommended).

The exception you're getting means that either myObject (the current user) is null or myObject.ProviderUserKey is null. I'd suggest that when you get to the breakpoint after myObject is set you should inspect the value of myObject and see whether it is null.
Depending on what is actually null affects where you look for the problem. If myObject is null then you'll need to look at the code to get the current user, and check whether someone is actually logged in etc. etc. If ProviderUserKey is null, consider whether you need this ID or would be better off with just using the username directly, check whether the membership provider actually provides that property in any meaningful way.

instead you can use this code directly....
string UserID = MembershipUser.ProviderUserKey.ToString();

Related

How to check of object exists in Google Cloud Storage using C#?

Does anyone know how to check if an object exists inside a Google Cloud Storage bucket via C#?
To test for the existence of an object without generating an exception when it isn't found, use the ListObjects() method.
The documentation on the prefix argument is a little misleading.
Only objects with names that start with this string will be returned.
In fact, the full object name can be used and will result in positive matches.
using( var client = StorageClient.Create() )
{
var results = client.ListObjects( "bucketname", "test.jpg" );
var exists = results?.Count() > 0;
return exists;
}
I believe protecting results w/ the nullable test is unnecessary. Even when I get no results with this code results is still not null. That said, I feel safer with that added protection since we are explicitly trying to avoid a try/catch.
Obviously this code could be shortened, but I wanted to demonstrate each step of the process for clarity.
This code was tested in a .net6 Console App using Google.Cloud.Storage.V1 version 3.7.0.
You can use Google.Cloud.Storage.V1 library
using Google.Cloud.Storage.V1;
public class StorageClass
{
public bool IsObjectExist(string bucketName, string objectname)
{
var client = StorageClient.Create();
return client.GetObject(bucketName, objectname) != null ? true : false;
}
}

Issue with WebMethod being Static in C# ASP.NET Codebehind

Due to a problem caused by having multiple forms on a single page, I used an AJAX call to a WebMethod to submit my form instead of using ASP controls. However, in doing this, the previous method I had used to create a new entry into my database no longer works because a WebMethod must be static.
I have authenticated my user already using ASPX authentication, and am trying to retrieve the username and ID of that user with codebehind. The user has already been authenticated on Page_Load, but it seems I cannot access this information through my WebMethod. Is this possible to do inside of a static WebMethod? Thank you for all of your help in advance!
[WebMethod]
public static void CreateJob()
{
Submit_Job();
}
public static void Submit_Job()
{
if (Page.User.Identity.IsAuthenticated)
{
try
{
string username = Context.User.Identity.Name;
}
catch
{
Context.GetOwinContext().Authentication.SignOut();
}
}
var manager = new UserManager();
var usernameDatabase = new ApplicationUser() { UserName = username };
usernameDatabase = manager.Find(username, "password here");
if (usernameDatabase != null)
{
IdentityHelper.SignIn(manager, usernameDatabase, isPersistent: false);
string jobTitle = Request.Form["jobTitle"];
using (var ctx = new CreateUserContext(ConfigurationManager.ConnectionStrings["myconnectionstring"].ConnectionString))
{
Job job = new Job()
{
job_title = jobTitle
};
ctx.Jobs.Add(job);
ctx.SaveChanges();
}
}
}
Edit:
There are errors for example with Page.User.Identity.IsAuthenticated -- Page, Context, and Request all appear that they cannot be static.
The specific error:
(An object reference is required for the non-static field, method, or property 'Control.Page') as well as with Context and Request.
Moving it from a simple comment
I had the same issue recently.
Luckily, whenever a user signs in our application, we store the user information encrypted into a session variable, so I retrieve that information, pass it to our user's class constructor, which decrypts it and I can use my logged in users info without a hassle.
So, my solution is to store the users info in the Session, but be careful what you store. Maybe serialize the users object and store in the session, then, whenever you need it
public void Page_Load()
{
// Retrieve authenticated user information
UserClass userObject = GetUserCredentials();
// Call a method that turns the authenticated user object into a string that contains the users session information. Given the sensivity of this information, might want to try to encrypt it or offuscate it. Store it in a session variable as a string
Session["UserContext"] = userObject.SerializeUser()
/* rest of the page code goes here */
}
[WebMethod(EnableSession=true)]
public static void CreateJob()
{
Submit_Job();
}
public static void Submit_Job()
{
// Lets get the authenticated user information through the session variable. Due to the static nature of the method, we can't access the Session variables directly, so we call it using the current HttpContext
string serializedUserInfo = )HttpContext.Current.Session["UserContext"].ToString();
// Let's create the users object. In my case, we have a overcharged constructor that receives the users serialized/encrypted information, descrypts it, deserializes it, and return a instance of the class with the deserialized information
UserClass userObject = new UserClass(serializedUserInfo);
// Do whatever the method has to do now!
}
On the subject of serialization, a quick google search with "c# object serialization" will bring you several good matches. XML and JSON are 2 of the most used kind of serialization, specially on web methods. Binary serialization is a good option to also obfuscate information of the logged in user

Custom IIS rewrite provider rewrite destination

Am writing a custom rewrite provider for IIS to send users to different sites depending on whether certain criteria are met. So if the user meets the criteria, the url does not get rewritten and the user can proceed as normal. If the user does not meet the criteria the url gets rewritten to load a different site, whilst appearing to be on the same url.
If set the rewrite section of the provider up like so the url is unaffected and the user is sent to the site as expected.
public string Rewrite(string value)
{
return value;
}
If set the rewrite section of the provider up like so the url is rewritten and the user is sent the alternate site.
public string Rewrite(string value)
{
return alternateSite;
}
If however I use the following set up if the user does not match the criteria they end up in the alternate site as expected. However if they do meet the criteria they end up in a redirect loop.
public string Rewrite(string value)
{
string newVal = alternateSite;
if (user != null)
{
if (user.status == 1)
{
newVal = value;
}
}
return newVal;
}
Any ideas how I can prevent this loop and have the site load correctly.
Thanks
EDIT Still no joy with this. I suspect if I could make the provider not perform the rewrite if the user meets the credentials (equivalent to setting the action to none on a standard rewrite rule) then it should continue to load normally. However I have no idea how to do this.
The problem with this was that the provider was only setting the variables for the provider when it was initialized, either that or they were being cached. These values were then being used for multiple sessions on multiple calls. This is fine for a constant like a connection string but not for something like a user id stored in a cookie. Instead I sent the user id through as part of the rewrite itself and split out it out in the provider logic as part of the Rewrite like so:
In IIS
{myProvider:{URL}+{HTTP_COOKIE}}
In provider
public string Rewrite(string value)
{
string[] values = value.Split('+');
string requestUrl = values[0];
string cookieStr = "";
if (values.Length > 1)
{
cookieStr = values[1];
}
...
This forces the provider to get fresh data each time and now the redirects go were they are supposed to.
Hope this is useful to someone

Users able to see others' details. FirstOrDefault returning the wrong record? Or caching issue? Help! :)

Users of my site have experienced some strange behaviour yesterday (first time I've seen this issue), and unfortunately I don't have much in the way of error logs to try to figure out what's going on. The site had a higher-than-normal number of people online at once, albeit not a large number in the grand scheme of things (maybe 50 to 100 users all trying to perform similar functions). I can't recreate the issue in my development environment, haven't seen it before, and don't really know why it is happening.
The crux of the problem is that users can register or log on successfully, but a small number of them could see other users' data.
The site is ASP.NET MVC 3.
Users are logging on and I set an authentication cookie - here's the LogOn action:
[HttpPost]
public ActionResult LogOn(AccountLogOnViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (!Membership.ValidateUser(model.UserName, model.Password))
{
ModelState.AddModelError("login-message", "Incorrect username or password");
}
}
if (ModelState.IsValid)
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
Session.Remove("MenuItems");
return Redirect(returnUrl ?? Url.Action("Index", "Home"));
}
else
{
model.ReturnUrl = returnUrl;
return View(model);
}
}
AccountLogOnViewModel is a simple object with two string properties, UserName and Password.
From what I can gather, this is fine - if you log in as NickW then doing something like User.Identity.Name correctly gives you "NickW" (when users were seeing other users' data, they reported that that "Welcome, NickW" text on screen was showing them the correct value - this is written out using User.Identity.Name)
The site also uses a custom membership provider. It overrides the ValidateLogin method, and the GetUser method. ValidateLogin appears to be working just fine so I'm not concerned about it.
The overridden GetUser method is as follows:
public override MembershipUser GetUser(string username, bool userIsOnline)
{
User user = _userRepository.Users.FirstOrDefault(u => u.UserName == username);
MembershipUser membershipUser = null;
if (user == null)
return membershipUser;
membershipUser = new MembershipUser(this.Name,
user.UserName,
user.Id,
user.Email,
null,
user.Comments,
user.IsActivated,
user.IsLockedOut,
user.CreatedDate,
user.LastLoginDate,
user.LastLoginDate,
user.LastModifiedDate,
Convert.ToDateTime(user.LastLockedOutDate));
return membershipUser;
}
So I'm attempting to retrieve a User object from my database, and using that to create a new MembershipUser object. My database User table has additional columns on top of those required by the membership provider - e.g. name, address, phone number etc.
At various points in the rest of the website (for example if you go to the Profile page), I retrieve a user object from the database and use it to populate the screen. The line I use to retrieve the User object is:
User user = userRepository.Users.FirstOrDefault(u => u.UserName == Membership.GetUser().UserName);
Here is a cut down version of the userRepository (i.e. just removing unrelated code).
public class SqlUserRepository : IUserRepository
{
private Table<User> usersTable;
private string _connectionString;
public SqlUserRepository(string connectionString)
{
_connectionString = connectionString;
usersTable = (new DataContext(connectionString)).GetTable<User>();
}
public IQueryable<User> Users
{
get { return usersTable; }
}
public void CreateUser(AccountRegisterViewModel user)
{
User newUser = new User();
newUser.UserName = user.UserName;
newUser.Salutation = user.Salutation;
newUser.PhoneNumber = user.PhoneNumber;
newUser.SecondaryPhoneNumber = user.SecondaryPhoneNumber;
newUser.FirstName = user.FirstName;
newUser.LastName = user.LastName;
newUser.PasswordSalt = CreateSalt();
newUser.Password = CreatePasswordHash(user.Password, newUser.PasswordSalt);
newUser.Email = user.Email;
newUser.CreatedDate = DateTime.UtcNow;
newUser.Comments = "Created from web registration";
newUser.LastModifiedDate = DateTime.UtcNow;
newUser.LastLoginDate = DateTime.UtcNow;
newUser.IsActivated = true;
newUser.IsLockedOut = false;
newUser.MayContact = user.MayContact;
usersTable.InsertOnSubmit(newUser);
usersTable.Context.SubmitChanges();
}
}
So it appears to me as if the auth cookie I'm setting is fine, but either:
When I first go in to the membership provider's GetUser() method, it retrieves the wrong record from the database and therefore sets up a MembershipUser object with the wrong username; subsequently when I look in the database for "this" user I'm actually looking for the wrong username.
Or: Intermittently when I do userRepository.FirstOrDefault(x => x.UserName == Membership.GetUser().Name) it retrieves the wrong record.
Or: something else is going wrong that I haven't thought of.
As I say, this seems to be a problem when the site was under load, so I'm wondering if it's some sort of caching issue somewhere? But I really don't know.
One thought I had was to change the way I retrieve the user in case the problem lies with the membership provider, and use this instead:
userRepository.FirstOrDefault(x => x.UserName == User.Identity.Name)
// or HttpContext.Current.User.Identity.Name if not within a controller
But really I'm not even sure what's going on so have no idea whether this will resolve the issue. Could it be a caching problem somewhere? It appears (but I can't be 100% certain) that when user A could see user B's details, it was always the case that user B was also active in the system (or had been within the previous 20 minutes).
I know it's a long shot, but does anyone have any idea how this could happen? Obviously it's a major concern and needs to be fixed urgently, but without knowing why it's happening I can't fix it!
Thanks in advance for any help,
Nick
Some things to consider:
Instead of using FirstOrDefault, use SingleOrDefault. FirstOrDefault assumes there will be more than 1 record of data matching your query. Since you are querying by username, there should only be 1 matching row, correct? In that case, use SingleOrDefault instead. When there are multiple rows that match the query, SingleOrDefault will throw an exception.
To get the username, instead of invoking Membership.GetUser().UserName, use User.Identity.Name. The User property on an MVC controller references an IPrincipal that should match the user's forms authentication cookie value. Since you have a custom membership provider, this should help eliminate its methods as a source of the problem.
There could be a caching issue if you have caching set up for the MVC project. Do you use the OutputCacheAttribute ([OutputCache]) on any controllers or action methods? Do you have it set up as a global filter in the global.asax file? Or do you think there may be some kind of SQL-based caching going on?
Looking at your overridden GetUser method, I see it should take 2 parameters: string username and bool isOnline. However, when you invoke it with Membership.GetUser().UserName, you are passing no parameters. Do you have another overridden overload of this method that also takes no parameters? What does it look like? Does it use System.Threading.CurrentPrincipal.Identity.Name to sniff out the current username when none is passed?

How to provide DirectoryEntry.Exists with credentials?

This morning I discovered a nice method (DirectoryEntry.Exists), that should be able to check whether an Active Directory object exists on the server. So I tried with a simple:
if (DirectoryEntry.Exists(path)) {}
Of course it lacks any overloads to provide credentials with it. Because, if credentials are not provided I get this Exception:
Logon failure: unknown user name or
bad password.
(System.DirectoryServices.DirectoryServicesCOMException)
Is there any other option that gives me the possibility to authenticate my code at the AD server? Or to check the existence of an object?
In this case you can't use the static method Exists as you said :
DirectoryEntry directoryEntry = new DirectoryEntry(path);
directoryEntry.Username = "username";
directoryEntry.Password = "password";
bool exists = false;
// Validate with Guid
try
{
var tmp = directoryEntry.Guid;
exists = true;
}
catch (COMException)
{
exists = false;
}
I know this is an old question, but the source code is now available so you can just Steal and Modify™ to make a version that accepts credentials:
public static bool Exists(string path, string username, string password)
{
DirectoryEntry entry = new DirectoryEntry(path, username, password);
try
{
_ = entry.NativeObject; // throws exceptions (possibly can break applications)
return true;
}
catch (System.Runtime.InteropServices.COMException e)
{
if (e.ErrorCode == unchecked((int)0x80072030) ||
e.ErrorCode == unchecked((int)0x80070003) || // ERROR_DS_NO_SUCH_OBJECT and path not found (not found in strict sense)
e.ErrorCode == unchecked((int)0x800708AC)) // Group name could not be found
return false;
throw;
}
finally
{
entry.Dispose();
}
}
The one change you must make is changing the use of Bind, since that's an internal method and can't be used by mere mortals like us. Instead, I just get the NativeObject property, which calls Bind() for us.
You can use that like this:
var ouExists = Exists("LDAP://hadoop.com/OU=Students,DC=hadoop,DC=com", "username", "password");
There is no way to do this and I have written a connect issue to hopefully resolve it.
DirectoryEntry.Exists Does Not Accept Credentials
Here you can read about impersonation in C#:
http://www.codeproject.com/KB/cs/zetaimpersonator.aspx
http://www.codeproject.com/KB/system/everythingInAD.aspx
So answer to the question: impossible.
Finally write an own method to get the DirectoryEntry by distinguised name, with credentials specified. In both cases of existence/inexistence I got an instance of DirectoryEntry. To check whether it's a valid object returned I do a simple try...catch to see if it results in an Exception. If so, it's invalid.
Nasty check, but it works. Too bad the default .net method DirectoryEntry.Exists doesn't provide an overload to provide credentials just like the DirectoryEntry constructor...
If the user who ran the process doesn't have permissions to call DirectoryEntry.Exists, then you can use impersonation.
This may be helpful (discusses impersonation in an AD context): http://www.codeproject.com/KB/system/everythingInAD.aspx
Btw, if you already have credentials of a user who has access to everything you need, why not just the process with that user (e.g. /runas)?

Categories