I'm using the code below to get various information about the logged in user and it works, but I want to turn it into a method where I can pass the attribute I want (DisplayName, EmailAddress, etc) and it returns that value for whoever is logged in. I couldn't figure out how to get this to work though.
using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
{
using (UserPrincipal user = UserPrincipal.FindByIdentity(context, User.Identity.Name))
{
if (user != null)
{
loggedInUserfullName = user.DisplayName;
loggedInUserEmail = user.EmailAddress;
}
}
}
While the above works, I can't use any of those variables outside of the scope of the method, which isn't helpful.
I tried the method below to just get the DisplayName but got errors due to FindByIdentity expecting a string (I don't have a string with the user's name - that's what I'm trying to find out!)
public string getUserFullName(object user)
{
user = UserPrincipal.FindByIdentity(ContextType.Domain, user);
string loggedInUserDisplayName = user.DisplayName;
return loggedInUserDisplayName ;
}
UserPrincipal loggedInUser = new UserPrincipal(ContextType.Domain);
// line above throws cannot convert from 'System.DirectoryServices.AccountManagement.ContextType' to 'System.DirectoryServices.AccountManagement.PrincipalContext'
getUserDetails(loggedInUser);
Is there a way to do this? Am I using the wrong library to achieve what I want?
This is a web application using windows authentication, if that makes any difference.
Thanks
I haven't used the DirectoryServices.AccountManagement stuff in a long time, but I'll give this a shot.
This line in your code is throwing an exception:
UserPrincipal loggedInUser = new UserPrincipal(ContextType.Domain);
The exception is telling you that the constructor for UserPrincipal expects a System.DirectoryServices.AccountManagement.PrincipalContext, but you're giving it a System.DirectoryServices.AccountManagement.ContextType. These lines in your working code are correct:
using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
{
using (UserPrincipal user = UserPrincipal.FindByIdentity(context, User.Identity.Name))
{
I'm not sure I fully understand what your intention is, but if you are looking for a reusable way to get info about the logged in user, try something like this:
public static class UserManager
{
public static string GetDisplayName(string name)
{
using (UserPrincipal user = UserPrincipal.FindByIdentity(new PrincipalContext(ContextType.Domain), name))
{
if (user != null)
{
return user.DisplayName;
}
throw new Exception("error");
}
}
}
You can call it by doing this:
var dn = UserManager.GetDisplayName(User.Identity.Name);
Obviously, you'll want to handle the error better. If I'm missing something, let me know and I'll try to update my answer.
Hope this helps!
Edit
To return an object containing multiple fields, you could do something like:
public static UserInfo GetDisplayName(string name)
{
using (UserPrincipal user = UserPrincipal.FindByIdentity(new PrincipalContext(ContextType.Domain), name))
{
if (user != null)
{
return new UserInfo
{
FullName = user.DisplayName,
Email = user.EmailAddress,
GivenName = user.GivenName,
SamAccountName = user.SamAccountName,
Surname = user.Surname
//any other things you may need somewhere else
};
}
throw new Exception("error");
}
}
Here is the UserInfo class:
public class UserInfo
{
public string FullName { get; set; }
public string Email { get; set; }
public string Surname { get; set; }
public string GivenName { get; set; }
public string SamAccountName { get; set; }
}
Related
I'm trying to create a class called User that can register new Users and store them in a list. Currently, I just want to be able to use the RegisterUser object. I have a C# book and have tried understanding it, but it's not clicking. Any help or hints in the right direction are very much appreciated.
namespace UserClass {
/// <summary>
/// The user class
/// <summary>
public class User {
public string userName;
public string password;
public string address;
public int contactNumber;
public static RegisterUser(string username, string pass, string add, int contact) {
User newUser = new User();
newUser.userName = username;
newUser.password = pass;
newUser.address = add;
newUser.contactNumber = contact;
WriteLine(newUser);
};
}
}
A few issues with your code:
WriteLine is incorrect unless you've created that method
I think you're looking for Console.WriteLine(...) which you can
use after adding using System; however even that would be
incorrect.
I assume you are looking to print the values of fields in
the User class which in that case, either override .ToString
(bad idea) or access and print them separately.
RegisterUser has no return type
It could be void -> public static void RegisterUser(string username, string pass, string add, int contact) indicating that nothing is returned.
However, common convention and expectation would be that the new User object is returned so that the caller can know what the final state of the operation was ->
You have a rogue ; at the end of the implementation of RegisterUser(...)
You need to remove it to make your code compile.
You actually have no variables anywhere, which would allow you to create a collection
You need to add your users to something like a List<User>, which would be encapsulated internally in another class perhaps called UserManager. This allows you to expose behaviour but hide the implementation, as well as adhering to SRP.
You could then expose the collection of users if you needed to, in a clear way, using a ReadOnlyCollection<User>.
This would show consumers that they would probably have to call a method (RegisterUser) to be able to add to the collection of users as ReadOnlyCollection<User> prevents modification, and not just do users.Add(...) & bypass your registration logic.
In this case, RegisterUser would also not have the static keyword, as it would need access to the instance field - your collection of users - and it won't be able to do that as a static method.
Your namespace is extremely specific to your User class
It's technically okay but namespaces are used to organise classes & control scope. I would rename it to something more related to your domain, as opposed to something bound to your class name (UserClass).
Arguments for RegisterUser
I would also cut down on the number of arguments to RegisterUser, take in a User object and then enforce all fields being set using the constructor for User.
This would turn it into a monadic method, making the code more readable, easier to test later on and makes you keep a conscious tab on how many "things" the method is responsible for.
Something like the below should work:
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace MyApplication
{
public class User
{
public string UserName;
public string Password;
public string Address;
public int ContactNumber;
public User(string username, string pass, string add, int contact)
{
UserName = username;
Password = pass;
Address = add;
ContactNumber = contact;
}
}
public class UserManager
{
private readonly List<User> _users = new List<User>();
public ReadOnlyCollection<User> GetUsers()
{
return _users.AsReadOnly();
}
public User RegisterUser(User newUser)
{
// process user, modify fields, add etc.
_users.Add(newUser);
return newUser;
}
}
public static class UserOutput
{
public static void WriteUserToConsole(User user)
{
Console.WriteLine($"{user.UserName}, {user.Password}, {user.Address}, {user.ContactNumber}");
}
}
}
var userManager = new UserManager();
var userToBeRegistered = new User("A", "B", "C", 0);
var createdUser = userManager.RegisterUser(userToBeRegistered);
UserOutput.WriteUserToConsole(createdUser);
var allUsers = userManager.GetUsers();
foreach (var user in allUsers)
UserOutput.WriteUserToConsole(user);
you are nearly there
public class User
{
public string userName;
public string password;
public string address;
public int contactNumber;
}
// Separate Class to create and store the users
public class UserController
{
// List to store your Users
public List<User> UserList;
// Constuctor instatiates UserList
public UserController()
{
UserList = new List<User>();
}
public void RegisterUser(string username, string pass, string add, int contact) {
User newUser = new User();
newUser.userName = username;
newUser.password = pass;
newUser.address = add;
newUser.contactNumber = contact;
// Adding the user to the UserList
UserList.Add(newUser);
// Show the userName of the new User in Console
Console.WriteLine(newUser.userName);
}
}
you can now Register users with your method in the new separate class. They will be stored in the UserList that you can access freely from outside of the class.
usage:
//get a UserController to start working
UserController controller = new UserController();
// call to RegisterUser
controller.RegisterUser("bob", "1234", "mystreet 1", 42);
I feel like you are close. See if the following points will help.
RegisterUser is a function/method with no purpose as it is missing a return type specification. Here you have two design options:
Create a new user and return a variable referencing this user.
static User RegisterUser( ... )
{
newUser = new User();
...
return newUser;
}
Create a new user and store the user internally to a list. This means the function specification must have void in its return type.
static List<User> userList = new List<User>();
static void RegisterUser( ... )
{
newUser = new User();
...
userList.Add(newUser);
}
You can specify the required information to define a user by declaring a constructor which assigns this information when a new User object is created. For example, if the username and password are the only required field then
Create a constructor accepting these two values
public class User
{
...
public User(string userName, string password)
{
this.userName = userName;
this.password = password;
}
}
Change the fields on readonly such that they cannot be modified at a later time.
public class User
{
public readonly string userName;
public readonly string password;
public string address;
public int contactNumber;
...
}
There is no defined way to display the information for each user on the console and so a call to Console.WriteLine(user) will only display the user type (default behavior of C#). To add this functionality to a class, override the ToString() method.
public class User
{
...
public override string ToString()
{
return $"User = {userName}, Address = {address}, Contact = {contactNumber}";
}
}
Now in the main program when you loop through the registered users, you can simply invoke Console.WriteLine() on each one to show on the screen the information.
static void Main(string[] args)
{
User.RegisterUser("aaa", "abc1", "100 east", 55500);
User.RegisterUser("xyz", "uvw5", "120 north", 55501);
foreach (var user in User.registededUsers)
{
Console.WriteLine(user);
}
}
it is common practice to hide fields behind properties. In this case it would be useful not to expose the list of registered users outside of the class as List<User> because it will allow the addition of new items in the list without requiring to call the RegisterUser() function. To disallow this behavior remove the public from the registededUsers field and add a property called RegisteredUsers expose this field as a IReadOnlyList<User>
public class User
{
...
static List<User> registededUsers = new List<User>();
public static IReadOnlyList<User> RegisteredUsers { get => registededUsers.AsReadOnly(); }
}
public async Task<IActionResult> Register(RegisterUserVM userVM)
{
if(!ModelState.IsValid) return View();
AppUser user = new AppUser
{
Name = userVM.Name,
Email = userVM.Email,
Surname = userVM.Surname,
UserName = userVM.Username
};
var result = await _userManager.CreateAsync(user,userVM.Password);
if (!result.Succeeded)
{
foreach (var item in result.Errors)
{
ModelState.AddModelError("", "ur username or password invalid");
return View();
}
}
return RedirectToAction("Index", "Home");
}
enter code here
public async Task<IActionResult> Login(LoginUserVM userVM)
{
if(!ModelState.IsValid) return View();
var user = await _userManager.FindByNameAsync(userVM.UsernameorEmail);
if(user == null)
{
user = await _userManager.FindByEmailAsync(userVM.UsernameorEmail);
if (user == null)
{
ModelState.AddModelError("", "ur username or password invalid");
return View();
}
}
var result = await _signInManager.PasswordSignInAsync(user, userVM.Password, userVM.IsPersistance, true);
if (!result.Succeeded)
{
ModelState.AddModelError("", "ur username or password invalid");
return View();
}
return RedirectToAction("Index","Home");
}
In WPF and therefore in the C# programming language, I am trying to use LDAP to find a specific user within an Active Directory. I was able to check if a specific user exists in an Active Directory but I was not able to retrieve that user from the directory in order to gain access to their properties.
I am using the System.DirectoryServices namespace.
Is there a way to do what I seek to achieve, is there a way to retrieve a specific user from the AD using LDAP in order to check their properties?
EDIT: code I used to check if user is in AD. Returns true if user is in AD, false if user is not found. I wonder thought if there is a limit to the number of users it will search.
bool ContainsUser(string domain, string userName)
{
string ldapBase = string.Format("LDAP://{0}", domain);
using (var entry = new DirectoryEntry(ldapBase))
{
using (var searcher = new DirectorySearcher(entry))
{
searcher.Filter = string.Format("(sAMAccountName={0})", userName);
return searcher.FindOne() != null;
}
}
}
You should investigate using UserPrincipal.FindByIdentity
for example:
public static string GetEmailAddressFromActiveDirectoryUserName(string adUserName)
{
string email = string.Empty;
if (!string.IsNullOrEmpty(adUserName))
{
using (var pctx = new PrincipalContext(ContextType.Domain))
{
using (UserPrincipal up = UserPrincipal.FindByIdentity(pctx, adUserName))
{
return !string.IsNullOrEmpty(up?.EmailAddress) ? up.EmailAddress : string.Empty;
}
}
}
return email;
}
See:
https://learn.microsoft.com/en-us/dotnet/api/system.directoryservices.accountmanagement.userprincipal.findbyidentity?view=netframework-4.8
The code you use to check that the user exists in AD or not already loads user properties: searcher.FindOne()?.Properties.
public class User
{
public string UserPrincipalName { get; set; }
public string Name { get; set; }
}
User GetAdUser(string domain, string userName)
{
string ldapBase = string.Format("LDAP://{0}", domain);
using (var entry = new DirectoryEntry(ldapBase))
{
using (var searcher = new DirectorySearcher(entry))
{
searcher.Filter = string.Format("(sAMAccountName={0})", userName);
var result = searcher.FindOne();
User user = null;
if (result != null)
{
// result.Properties - list of loaded user properties
// result.Properties.PropertyNames - list of user property names
user = new User
{
UserPrincipalName = result.Properties["userprincipalname"].Cast<string>().FirstOrDefault();
Name = result.Properties["name"].Cast<string>().FirstOrDefault();
}
}
return user;
}
}
}
I have a method that creates a new user and then insert a row in the User Permission table, but nothing is happening. Here is my code:
// before: creates user
var permission = new UserPermission()
{
UserId = user.Id,
UserName = user.UserName,
Assets = createUserModel.Assets
};
AccountDb.UserPermissions.Add(permission);
var saveChangesResult = AccountDb.SaveChanges();
if (saveChangesResult == 0) // the result is always 0
{
AppUserManager.Delete(user);
return BadRequest("User permission could not be saved");
}
// then: add user to role and return ok
SaveChanges always returns 0 and doesn't update the database, I've already googled for it and tried the following actions:
// no success
AccountDb.UserPermissions.Attach(permission);
// no success either
AccountDb.Entry(permission).State = EntityState.Added;
And I tried in async method too, but no luck.
Here is my "UserPermission" model:
public class UserPermission
{
public int Id { get; set; }
public string UserId { get; set; }
public string UserName { get; set; }
public string _Assets { get; set; }
[NotMapped]
public List<int> Assets
{
get
{
if (_Assets == null) return null;
return Array.ConvertAll(_Assets.Split(';'), Int32.Parse).ToList();
}
set
{
_Assets = String.Join(";", value.Select(x => x.ToString()).ToArray());
}
}
}
It's curious because this method was working lately, but after some changes in "AccountContext" and some rolebacks, I notice that the method wasn't working anymore.
-- edit --
here is the full create method
[HttpPost]
[Route("create")]
public IHttpActionResult CreateUser(CreateUserBindingModel createUserModel)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (createUserModel.RoleName is null)
{
return BadRequest("There is no role assigned to user");
}
var user = new UserModel()
{
UserName = createUserModel.UserName,
Email = createUserModel.Email,
FirstName = createUserModel.FirstName,
LastName = createUserModel.LastName
};
var addUserResult = AppUserManager.Create(user, createUserModel.Password);
if (!addUserResult.Succeeded)
{
return GetErrorResult(addUserResult);
}
var permission = new UserPermission()
{
UserId = user.Id,
UserName = user.UserName,
Assets = createUserModel.Assets
};
AccountDb.UserPermissions.Add(permission);
var saveChangesResult = AccountDb.SaveChanges();
if (saveChangesResult == 0)
{
AppUserManager.Delete(user);
return BadRequest("User permission could not be saved");
}
var addRoleResult = AppUserManager.AddToRole(user.Id, createUserModel.RoleName);
if (!addRoleResult.Succeeded)
{
AppUserManager.Delete(user);
return GetErrorResult(addUserResult);
}
return Ok(TheModelFactory.Create(user));
}
-- edit 2 --
I was using an Azure db string and I changed to a local db string, but the problem still is the same, UserPermissions Table does not update and no error is emiting.
You are creating a new user, but you never use the result. Your user.Id is always null (or the respective default).
I don't know why your database fails silently (constraints or foreign keys should throw an exception), but you should fix that first. It will probably result in real records being written.
I found a solution!
For some reason, my local instance of AccountContext was the problem, it gets all UserPermissions but doesn't update. So I created a new instance and tried to add the permission:
using (var db = new AccountContext())
{
db.UserPermissions.Add(permission);
var saveChangesResult = db.SaveChanges();
if (saveChangesResult == 0)
{
AppUserManager.Delete(user);
return BadRequest("User permission could not be saved");
}
}
now it's working well. But I don't know why the local AccountDb is not working now if it was working in the past
I created a class User, which contain simple variables as shown below:
public class User
{
public string username; //Unique usernames
public string password;
}
I then instantiate a list of an object in another class:
List<User> user = new List<User>();
user.Add(new User {username = "admin", password = "123"});
How is it possible for me to retrieve the password's value by searching for the username using a foreach loop? I am probably just confused but this is what I came up with:
foreach(var item in user)
{
if(item.Equals(username_input))
{
//I try to store the password into a string pass_check
pass_check = item.password;
}
}
if (user_input.Equals(pass_check))
{
Console.WriteLine("Login successful");
}
Sorry if this seems like a dense question to anyone out there, still a beginner trying to learn!
You're pretty close..
if(item.username.Equals(username_input))
You need to check the property of the item in this case which is username.
You could even shorten it to:
foreach(var item in user)
{
if(item.username.Equals(username_input)
&& user_input.Equals(item.password))
{
Console.WriteLine("Login successful");
break; // no need to check more.. or is there?
}
}
You can get really fancy using Linq:
if (user.Any(i => i.username.Equals(username_input)
&& user_input.Equals(i.password))
{
Console.WriteLine("Login successful");
}
As juharr noted in the commend, best practices for exposing values from class/objects is to use Properties not Fields..
public class User
{
// not best practices
public string username;
// best practices
public string password { get; set; }
}
Even fancier:
using System.Linq;
public static class Extensions
{
// Extension method that works on List<User> to validate user && PW
// - returns true if user exists and pw is ok, else false
public static bool CheckUserPassword(this List<User> users, string user_name, string pw)
{
// add null checks for users, user_name and pw if you are paranoid of know your customers ;o)
return users.Any(u => u.username == user_name && u.password == pw);
}
}
public class User
{
public string password;
public string username; //Unique usernames
}
internal class Program
{
private static List<User> users = new List<User> { new User { username = "admin", password = "123" } };
static void Main(string[] args)
{
// use extension method like this:
var isValid = users.CheckUserPassword("Hello","Jeha");
Console.WriteLine($"user 'admin' with pw '8888' => {users.CheckUserPassword("admin", "8888")}");
Console.WriteLine($"user 'admin' with pw '123' => {users.CheckUserPassword("admin", "123")}");
Console.ReadLine();
}
}
Extension Methods can be executed on the this-part - in this case only on List<User>s. The Extensionmethod uses Linq to find if Any (at least 1) user of this name and pw exists.
I need help with accessing class properties within a given class.
For example, take the below class:
public partial class Account
{
private Profile _profile;
private Email _email;
private HostInfo _hostInfo;
public Profile Profile
{
get { return _profile; }
set { _profile = value; }
}
public Email Email
{
get { return _email; }
set { _email = value; }
}
public HostInfo HostInfo
{
get { return _hostInfo; }
set { _hostInfo = value; }
}
In the class "Account" exists a bunch of class properties such as Email or Profile.
Now, when I want to access those properties at run-time, I do something like this
(for Email):
_accountRepository = ObjectFactory.GetInstance<IAccountRepository>();
string username = Cryptography.Decrypt(_webContext.UserNameToVerify, "verify");
Account account = _accountRepository.GetAccountByUserName(username);
if(account != null)
{
account.Email.IsConfirmed = true;
But, I get "Object reference not set..." for account.Email... Why is that?
How do I access Account such that account.Email, account.Profile, and so on
returns the correct data for a given AccountId or UserName.
Here is a method that returns Account:
public Account GetAccountByUserName(string userName)
{
Account account = null;
using (MyDataContext dc = _conn.GetContext())
{
try
{
account = (from a in dc.Accounts
where a.UserName == userName
select a).FirstOrDefault();
}
catch
{
//oops
}
}
return account;
}
The above works but when I try:
account = (from a in dc.Accounts
join em in dc.Emails on a.AccountId equals em.AccountId
join p in dc.Profiles on em.AccountId equals p.AccountId
where a.UserName == userName
select a).FirstOrDefault();
I am still getting object reference exceptions for my Email and Profile
properties. Is this simply a SQL problem or is there something else I need to be
doing to be able to fully access all the properties within my Account class?
Thanks!
Your getting this because Email is another class which has not been assigned yet. What you can do is in your constructor default the properties that link to other classes as new items. For example in your ctor:
public Account()
{
// Set Defaults
Email = new Email();
Profile = new Profile();
HostInfo = new HostInfo();
}
Then you can set their values as desired.
This looks like a case of handling null values on your properties. You should initialize the Email property to something other than null if you expect to store or query against it, or alter the queries so that they can expect to deal with null values. Also if you get a null value from the database, and your property cannot be set to null, the reverse problem occurs.
Are you declaring these properties yourself, or are you trying to indicate something like auto-generated code from like Linq-to-SQL?
If this is auto-generated where the Account table references the Email table, etc., then you probably just need to specify that you want those objects to load as well in the load options:
using (MyDataContext dc = _conn.GetContext())
{
var options = new DataLoadOptions();
options.LoadWith<Account>(a => a.Email);
options.LoadWith<Account>(a => a.Profile);
options.LoadWith<Account>(a => a.HostInfo);
dc.LoadOptions = options;
try
{
account = (from a in dc.Accounts
where a.UserName == userName
select a).FirstOrDefault();
}
catch
{
//oops
}
}
Just wanted to add: there is now a shorter form for declaring trivial properties:
public Profile Profile { get; set; }
public Email Email { get; set; }
public HostInfo HostInfo { get; set; }