C# - Correct approach for method with various validations - c#

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.

Related

How to call a function from another script?

I've read a bunch of guides on how to do this, but being new to programming I'm not really getting it.
I have a script called DatabaseHandler where I wrote a new class and function to write some basic user information to a Firebase database when someone creates a new account.
DatabaseReference userRef = FirebaseDatabase.DefaultInstance.RootReference;
public class User
{
public string email;
public int score;
public int round;
public int lives;
public User()
{
}
public User(string email)
{
this.email = email;
this.score = 0;
this.round = 1;
this.lives = 3;
}
}
public void writeNewUser(string email, int score, int round, int lives)
{
User user = new User(email);
string json = JsonUtility.ToJson(user);
userRef.Child("users").Child(email).SetRawJsonValueAsync(json);
}
Also, I have another script called LoginHandler that calls CreateUserAsync() when someone click the button to create an account. I'm using a code snippet I found on a guide online for this part, and am trying to figure out where and how I can call the writeNewUser function that's written in DatabaseHandler from within LoginHandler? Should I call it from Task HandleCreateUserAsync after the auth.CurrentUser != null to make sure the data is only written if the username has not already been created?
public void CreateUserAsync() {
DebugLog(String.Format("Attempting to create user {0}...", email));
// This passes the current displayName through to HandleCreateUserAsync
// so that it can be passed to UpdateUserProfile(). displayName will be
// reset by AuthStateChanged() when the new user is created and signed in.
string newDisplayName = displayName;
auth.CreateUserWithEmailAndPasswordAsync(email, password)
.ContinueWith((task) => {
return HandleCreateUserAsync(task, newDisplayName: newDisplayName);
}).Unwrap();
}
Task HandleCreateUserAsync(Task<Firebase.Auth.FirebaseUser> authTask,
string newDisplayName = null) {
if (LogTaskCompletion(authTask, "User Creation")) {
if (auth.CurrentUser != null) {
DebugLog(String.Format("User Info: {0} {1}", auth.CurrentUser.Email,
auth.CurrentUser.ProviderId));
return UpdateUserProfileAsync(newDisplayName: newDisplayName);
}
}
// Nothing to update, so just return a completed Task.
return Task.FromResult(0);
}

how pass parameter from IsAuthorized to HandleUnauthorizedRequest Method web api

I extended AuthorizeAttribute and based on some condition I want display different messages and httpStatusCode to the user. My code is:
protected override bool IsAuthorized(HttpActionContext actionContext)
{
string headerApiKeyValue = string.Empty;
try
{
IEnumerable<string> headerValues;
bool isExitsInHeader = actionContext.Request.Headers.TryGetValues("ApiKey",out headerValues);
if (!isExitsInHeader || headerValues == null)
{
actionContext.Response = CreateResponse(HttpStatusCode.BadRequest, apiKeyNotExistInHeader);
return false;
}
return true;
}
catch (Exception exception)
{
// log exception [apiKeyValue]
return false;
}
}
protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
{
string a = actionContext.Request.ToString();
// log here
actionContext.Response = CreateResponse(HttpStatusCode.MethodNotAllowed, accessDeniedMessage);
}
In above code there is two response types: First is when the key dos not exist in the header and the second is IsAuthorized method throw exception. How to handle this two types of messages in HandleUnauthorizedRequest method? Is there any way to pass parameter to this method?
update: (I just realized this question is almost 2 years old by now, so I don't assume, we'll here back from the op)
You can just use a private field to store any information you need. Here is a really dead simple example demonstrating this:
public class HeaderAuthenticationAttribute : AuthorizeAttribute
{
private bool invalidApiKey = false;
protected override bool IsAuthorized(HttpActionContext actionContext)
{
bool isExitsInHeader = actionContext.Request.Headers.TryGetValues("ApiKey", out IEnumerable<string> headerValues);
if (isExitsInHeader && headerValues.Any(x => x.Contains("foo")))
{
return true;
}
else if (isExitsInHeader && !headerValues.Any(x => x.Contains("foo")))
{
return false;
}
else
{
invalidApiKey = true;
return false;
}
}
protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
{
//check if response has header indicating invalid api key
if (invalidApiKey)
{
actionContext.Response = new HttpResponseMessage()
{
StatusCode = HttpStatusCode.MethodNotAllowed,
Content = new StringContent("errorMessage here")
};
}
else
{
actionContext.Response = new HttpResponseMessage()
{
StatusCode = HttpStatusCode.Forbidden,
Content = new StringContent("someOther message here")
};
}
// log here
}
}
What this does (and it is not really a real world example) is the following:
Check if the header "ApiKey" is present
If it is and any of those results is "foo", we're golden
If it is, but no value is "foo", return false
If no header is present, set the invalid header field to true and return false
The HandleUnauthorizedRequest then just checks if this private field is set or not and uses it as a switch to return different HttpResponseMessages with different Status Codes to the user.
In the end you onjly need to implement your logic to validate the api Key and return the appropriate messages as per your requirements.
throw new HttpResponseException(HttpStatusCode.Unauthorized);
Try this & pass the exception you want

Which is the best way/pratice to work this scenario? Have I to use session?

(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?

Issue with Login

Struggling Newbie :(
Entity not fetching the User..
Login Controller
[HttpPost]
public ActionResult LoginForm(string user_NA, string user_pwd)
{
User u = new User();
u.LogIn(user_NA, user_pwd);
return RedirectToAction("Index");
}
Login Model
public bool LogIn(string userID, string password)
{
using (Entity.xcLEntities en = new xcLEntities())
{
try
{
var user = en.Users.SingleOrDefault(x => x.LoginID == userID && x.PasswordHash == this.SetPassword(password));
if (user != null && password != null)
{
return true;
}
else
{
return false;
}
}
catch (Exception e)
{
throw e;
}
}
}
Exception shoing
An exception of type 'System.NotSupportedException' occurred in AML.Web.dll but was not handled in user code
Additional information: LINQ to Entities does not recognize the method 'System.String SetPassword(System.String)' method, and this method cannot be translated into a store expression.
Regarding your exception, you could try getting the corresponding hash outside the SingleOrDefault method:
public bool LogIn(string userID, string password)
{
using (Entity.AMLEntities en = new AMLEntities())
{
try
{
string hashed = this.SetPassword(password);
var user = en.Users.SingleOrDefault(x => x.LoginID == userID && x.PasswordHash == hashed);
if (user != null && password != null)
{
return true;
}
else
{
return false;
}
}
catch (Exception e)
{
throw e;
}
}
}
EDIT: As suggested by Rezoan in the comments, more information can be found by reading Entity Framework can't run your C# code as part of its query.
You should store the result of the SetPassword methon in a local varoable and use the varoable in the lambda expression. The exception is clearly state that the setpassword method is not avaliable on the db side.

Username and role

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");

Categories

Resources