I have this databases: table<User>(UserID,Name,Surname,Username,Password,Email), table<Role>(RoleID,RoleName,Description), and table<UsersInRole>(UserID,RoleID). I create a login authentication with username and password to access to the application (with Linq ToSql to store data), and it is right.
Now I wish to create a role for each user, but I don't know how work out it; I saw some features about it but it refers to web.app.
This is the code of the procedure that applies to login:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
public bool ValidateApplicationUser(string userName, string password)
{
{
var AuthContext = new DataClasses1DataContext();
var query = from c in AuthContext.Users
where (c.Username == userName.ToLower() && c.Password == password.ToLower())
select c;
if(query.Count() != 0 )
{
return true;
}
return false;
}
}
private void mahhh(object sender, RoutedEventArgs e)
{
bool authenticated = true;
{
if (usernameTextBox.Text !="" && passwordTextBox.Text != "")
{
authenticated = ValidateApplicationUser(usernameTextBox.Text , passwordTextBox.Text);
}
}
if (!authenticated)
{
MessageBox.Show("Invalid login. Try again.");
}
else
{
MessageBox.Show("Congradulations! You're a valid user!");
Window3 c = new Window3();
c.ShowDialog();
this.Close();
}
}
}
I don't know how to implement a method to assign a role to the user.
Do you have any idea or suggest to make it right?
First, try not to store passwords in the database; it is better to store a hash. I'm not quite sure what you mean "assign a role to the user" - are you having difficulty getting the role from the db? Or are you unsure what to do with it afterwards? If the latter, the "principal" is the way to go; at the simplest level:
string username = ...
string[] roles = ...
Thread.CurrentPrincipal = new GenericPrincipal(
new GenericIdentity(username), roles);
Now you can use role-based security, either declarative or imperative.
Declarative:
[PrincipalPermission(SecurityAction.Demand, Role="ADMIN")]
public void Foo()
{ // validated automatically by the .NET runtime ;-p
}
Imperative:
static bool IsInRole(string role)
{
IPrincipal principal = Thread.CurrentPrincipal;
return principal != null && principal.IsInRole(role);
}
...
bool isAdmin = IsInRole("ADMIN");
Related
I want to know what is the correct way of doing this: lets say I have a login method that receives username and password, and log ins the user or return invalid username/password or not enough permissions. What is the correct way of doing this?
Way #1: throwing exception and handling in the user interface to display the error
public void Login(string username, string password)
{
if (SessionService.GetSession.IsLoggedIn)
{
throw new Exception("User is already logged in");
}
var user = GetByUsername(username);
if (user == null)
{
throw new LoginException(LoginResultEnum.InvalidUsername);
}
var hashPass = EncryptionService.Hash(password);
if (hashPass != user.password)
{
throw new LoginException(LoginResultEnum.InvalidPassword);
}
if (!user.HasPermission(PermissionTypeEnum.CanLogIn))
{
throw new MissingPermissionException(TipoPermisoEnum.CanLogIn);
}
SessionService.GetSession.Login(user);
}
Way #2: returning boolean true/false and handle the error in the UI (success or fail)
public bool Login(string username, string password)
{
if (SessionService.GetSession.IsLoggedIn)
{
return false;
}
var user = GetByUsername(username);
if (user == null)
{
return false;
}
var hashPass = EncryptionService.Hash(password);
if (hashPass != user.password)
{
return false;
}
if (!user.HasPermission(PermissionTypeEnum.CanLogIn))
{
return false;
}
SessionService.GetSession.Login(user);
return true;
}
Way #3: returning a LoginResult enum and handle in the UI
public LoginResult Login(string username, string password)
{
if (SessionService.GetSession.IsLoggedIn)
{
return LoginResult.AlreadyLoggedIn;
}
var user = GetByUsername(username);
if (user == null)
{
return LoginResult.InvalidUsername;
}
var hashPass = EncryptionService.Hash(password);
if (hashPass != user.password)
{
return LoginResult.InvalidPassword;
}
if (!user.HasPermission(PermissionTypeEnum.CanLogIn))
{
return LoginResult.Forbidden;
}
SessionService.GetSession.Login(user);
return LoginResult.OK;
}
In my view it better to create some dto if it is eligible for your case. So this dto will have the following properties:
public class LoginResponseDto
{
public bool Success { get; set; }
public string Error { get; set; }
}
And then you will return your response something like this:
public LoginResponseDto Login(string username, string password)
{
if (SessionService.GetSession.IsLoggedIn)
{
return new LoginResponseDto { Error = "User is already logged in" };
}
var user = GetByUsername(username);
if (user == null)
{
return new LoginResponseDto { Error = "There is no such user" };
}
var hashPass = EncryptionService.Hash(password);
if (hashPass != user.password)
{
return new LoginResponseDto { Error = "Incorrect password or username" };
}
if (!user.HasPermission(PermissionTypeEnum.CanLogIn))
{
return new LoginResponseDto { Error = "There is no permission to log in" };
}
SessionService.GetSession.Login(user);
return new LoginResponseDto { Success = true };
}
It is possible to see this tutorial "Create a secure ASP.NET MVC 5 web app with log in, email confirmation and password reset". Author of article use ViewBag in this article to send errors from controller and Succeeded to check whether login is okay.
In addition, try to avoid to show message about what is exactly wrong username or password.
I would say #3 is the best way.
#1 you are using Exception for non-exceptional circumstances. The control path is expected, so don't use Exceptions.
#2 By using a bool you are discarding information, is it InvalidPassword or Forbidden?
#3 Returns all information, allowing the UI to surface that information to the User.
(Anyone can help me with a best title for this question? I try to think... but I don't have any success with best one than that.)
Let me explain my following scenario and doubt.
I'm creating a website that will manage a lot of projects per user. Each user can be a owner or a participant only.
I have common areas for logged users, areas that all users from a project can access, and areas that only the owner can access.
I create a OwnerAuthorize attribute and a SimpleAuthorize attribute to Controllers and Actions:
public class OwnerAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var projectId = int.Parse(httpContext.Request.Params.Get("projectId"));
var userId = httpContext.User.Identity.GetUserId<int>();
project currentProject = null;
using (var projectService = new ProjectsService())
{
currentProject = projectService.Find(projectId).Result;
}
if (currentProject == null)
{
return false;
}
else
{
if (currentProject.OwnerId == userId)
{
return true;
}
else
{
return false;
}
}
}
}
public class SimpleAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var projectId = int.Parse(httpContext.Request.Params.Get("projectId"));
var userId = httpContext.User.Identity.GetUserId<int>();
Project currentProject = null;
using (var projectService = new ProjectsService())
{
currentProject = projectService.Find(projectId).Result;
}
if (currentProject == null)
{
return false;
}
else
{
if (currentProject.OwnerId == userId
|| currentProject.Users.Select(u => u.Id).Contains(userId))
{
return true;
}
else
{
return false;
}
}
}
}
For few users, it's ok. But for many users, I have a database query to gather the same information for each action called. So, I was thinking to use a session to keep some informations of the current user and project accessed.
Which is the best way/pratice to work with this scenario?
We have an internal facing (C#/MVC/IIS7) application for which the requirement is for users to not have to enter credentials, but we need the network/Windows username to identify who the current user is.
Is there any way to accomplish this? Thanks!
Here's some code that deals with that kind of thing:
public class WindowsIdentityHelper
{
public WindowsPrincipal GetWindowsPrincipal()
{
//Get Current AppDomain
AppDomain myDomain = Thread.GetDomain();
myDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
return (WindowsPrincipal)Thread.CurrentPrincipal;
}
public bool IsUserBelongsToWindowsAdministratorGroup()
{
WindowsPrincipal myPrincipal = GetWindowsPrincipal();
if (myPrincipal.IsInRole("Administrators"))
return true;
if (myPrincipal.IsInRole(WindowsBuiltInRole.Administrator))
return true;
else
return false;
}
public string GetFullDomainLoginUserName()
{
WindowsPrincipal myPrincipal = GetWindowsPrincipal();
return myPrincipal.Identity.Name.ToString();
}
public string GetLoginUserName()
{
string authenticatedUser = string.Empty;
string userName = GetFullDomainLoginUserName();
if (userName.Contains("\\"))
authenticatedUser = userName.Split('\\')[1];
else
authenticatedUser = userName;
return authenticatedUser;
}
}
This works for me (ASP.NET C#):
string username = Request.LogonUserIdentity.Name;
In my ASP.NET C# website I am attempting to use Sessions to allow users to log in and navigate throughout the secure pages while the session is valid. If for whatever reason they timeout or sign out, they are to be redirected to the landing page. Currently the site only allows one user to be logged in at a time. It seems apparent that the session information is being stored incorrectly, but I don't understand where or why this occurs. If you access the page using another browser, you can see the code pulling information out of the session (like the username) that it should not know.
I want to allow multiple valid users to be logged in simultaneously and have no adverse affect on each other while doing so. If you need further information than the code samples I post below, please ask.
My Login page:
//Login.ascx.cs
...
private void Login_Click(object sender, EventArgs e)
{
if (sender == null || e == null)
{
throw new ArgumentNullException("Null Exception: Login_Click");
}
User user = new User();
user.Login(_username.Text, _password.Text);
if (user.IsValid() && user.GetIsUser() != false)
{
user.Save();
Session["Username"] = _username.Text;
Response.Redirect("Secure/Default.aspx");
}
else
{
DisplayErrors(user._validationErrors);
}
_errors.Text = errorMessage;
}
The welcome page (first secure page a user sees)
private void Page_Load(object sender, System.EventArgs e)
{
Business.User user = new Business.User();
_labelUsername.Text = MySession.Current.Username;
_labelCompanyId.Text = MySession.Current.CompanyId;
redirectLogin = "../Default.aspx";
//redirect if any conditions fail for user validation.
if (sessionKey != MySession.GetSessionId() || (string)Session["Username"] != _labelUsername.Text)
{
Response.Redirect(redirectLogin);
}
else
{
Debug.WriteLine("Welcome SUCCESS: " + _labelUsername.Text);
Debug.WriteLine("Welcome " + sessionKey);
}
}
And finally the User page that includes SQL Query
public static string dataUsername;
public static string dataCompanyId;
private const string _getUserByUsernameQuery =
#"SELECT [User].[username], [Company].[name]
FROM [User] WITH (NOLOCK) INNER JOIN [Company] WITH (NOLOCK)
ON [User].[companyId] = [Company].[id]
WHERE [User].[username] = #username AND [User].[password] = #password";
private string _username;
private string _companyid;
public User(){}
public void Login (string username, string password)
{
using (SqlConnection connection = new SqlConnection(SQLConfiguration.ConnectionString))
{
SqlCommand command = new SqlCommand(_getUserByUsernameQuery, connection);
command.Parameters.AddWithValue("#username", username);
command.Parameters.AddWithValue("#password", password);
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
if (reader.Read())
{
Username = Convert.ToString(reader["username"]);
CompanyId = Convert.ToString(reader["name"]);
dataUsername = Username;
dataCompanyId = CompanyId;
}
}
}
}
#region Properties
public string Username
{
get{ return _username; }
set{ _username = value;}
}
public string CompanyId
{
get{ return _companyid;}
set{ _companyid = value;}
}
#endregion
EDIT: In response to some of the questions:
//in the first accessed page for secure users, before 'Page_load'
public static string sessionKey
{
get
{
return MySession.GetSessionId();
}
}
...
//in my 'MySession' class
public static string GetSessionId()
{
return System.Web.HttpContext.Current.Session.SessionID;
}
See Static Classes and Static Class Members (C# Programming Guide)
A static constructor is only called one time, and a static class remains in memory for the lifetime of the application domain in which your program resides.
The code-
public static string GetSessionId()
{
return System.Web.HttpContext.Current.Session.SessionID;
}
Will return the same SessionID for the lifetime of the application.
Like other posters I would strongly recommend you use the ASP.NET built in membership providers rather than try and invent your own, this will be more secure, more widely understood and thus supported, easier to maintain and more extendable.
Just use ASP.NET Forms Authentication. I bet all anonymous users are sharing the same session object. You can hook it into your own authentication scheme if something already exists. (passwords in a database or file, for instance)
Following up from this question:
designing application classes
What is wrong (from a design point of view) with this class:
I'm trying to refactor this class and it's abstract base class (Logon) and the fact it's actually horrible design. I wrote it myself (pretty much when I was a newbie). I'm finding it hard to refactor and want some input on it?
class NewUserLogon : Logon, ILogonNewUser, IDisposable
{
#region Member Variables
System.Windows.Forms.Form _frm = new MainWindow();
SQLDatabase.SQLDynamicDatabase sql;
SQLDatabase.DatabaseLogin dblogin;
LogonData lgndata;
System.Security.SecureString securepassword;
PasswordEncrypt.Collections.CreatedItems items;
LogonEventArgs e = new LogonEventArgs();
#endregion
#region Constructors
// for DI
public NewUserLogon(PasswordEncrypt.Collections.CreatedItems items) : base (items)
{
this.items = items;
}
#endregion
#region Public Methods
public new void Dispose()
{
}
public bool? ReadFromRegistry(HashedUsername username, HashedPassword hashedpassword)
{
return RegistryEdit.ReadFromRegistry(username, hashedpassword);
}
public bool WriteToRegistry(HashedUsername username, HashedPassword hashedpassword)
{
return RegistryEdit.WriteToRegistry(username, hashedpassword);
}
public override void Login(TextBox username, TextBox password)
{
base.Login(username, password);
Login(username.Text, password.Text);
}
#endregion
#region Protected Methods
protected override void Login(string username, string password) // IS INSECURE!!! ONLY USE HASHES
{
base.Login(username, password);
if (_user is NewUserLogon) // new user
{
sql = new PasswordEncrypt.SQLDatabase.SQLDynamicDatabase();
dblogin = new SQLDatabase.DatabaseLogin();
lgndata = base._logondata;
securepassword = new System.Security.SecureString();
// Set Object for eventhandler
items.SetDatabaseLogin = dblogin;
items.SetSQLDynamicDatabase = sql; // recreates L
items.Items = items;
string generatedusername;
// write new logondata to registry
if (this.WriteToRegistry(lgndata.HahsedUserName, lgndata.HashedPsw))
{
try
{
// Generate DB Password...
dblogin.GenerateDBPassword();
// get generated password into securestring
securepassword = dblogin.Password;
//generate database username
generatedusername = dblogin.GenerateDBUserName(username);
if (generatedusername == "Already Exists")
{
throw new Exception("Username Already Exists");
}
//create SQL Server database
try
{
sql.CreateSQLDatabase(dblogin, username);
}
catch (Exception ex)
{
//System.Windows.Forms.MessageBox.Show(ex.Message);
e.ErrorMessage = ex.Message;
e.Success = false;
OnError(this, e);
}
}
catch (Exception exc)
{
e.Success = false;
e.ErrorMessage = exc.Message;
OnError(this, e);
}
OnNewUserLoggedIn(this, e); // tell UI class to start loading...
}
else
{
System.Windows.Forms.MessageBox.Show("Unable to write to Registry!", "Registry Error", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Exclamation);
}
}
else if (_user is ExistingUserLogon) // exising user
{
bool? compare = base._regRead;
lgndata = base._logondata;
if (compare == true)
{
//Tell GUI to quit the 'busydialog' thread
OnMessage(this, e);
LogonFrm frm = LogonFrm.LogonFormInstance;
// tell user he already exists and just needs to login
// ask if user wants to logon straight away
System.Windows.Forms.DialogResult dlgres;
dlgres = System.Windows.Forms.MessageBox.Show("Your login already exists, do you wan to login now?", "Login Exists", System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Question);
if (dlgres == System.Windows.Forms.DialogResult.Yes)
{
ExistingUserLogon existinguser = new ExistingUserLogon(compare, lgndata);
existinguser.Error += new ErrorStatus(frm._newuser_Error);
existinguser.loginname = username;
existinguser.LoginNewUser();
///TELL GUI THAT USER LOGIN SUCCEEDED, THROUGH EVENT
e.Success = true;
OnNewUserLoggedIn(this, e);
}
else
{
e.Success = false;
e.ErrorMessage = "Failed";
OnError(this, e);
}
}
}
}
#endregion
}
Your class tries to do too many things. Try to separate different responsibilities into separate classes (eg database access and UI stuff).
And why are you instantiating a new Form at the beginning of your class and don't seem to use it further on?
Your protected Login is way too long.
Security should be a cross cutting concern, not a base class. I don't know if you have aspect oriented programming techniques available to you, but extending a base class with security built into it seems like an abuse of inheritance to me.