How to refactor procedural code? - c#

I am creating a password reset feature for web site. First step for password reset have to be implemented:
User enters his email in password reset form.
System checks if user with email is registered.
If user has been found, system sends email with password reset URL with uniqe token.
If user is not found, system sends email notifying that password reset was initiated for this email, but user account doesn't exist.
I have service class which implements public method - RequestPasswordReset and this method is quite procedural:
public void RequestPasswordReset(string email)
{
if(!IsValidEmail(email))
{
throw new ArgumentException("email");
}
var user = this.repository.FindByEmail(email);
if(user != null)
{
user.PasswordResetToken.Set(this.tokenGenerator.NewToken());
this.emailService.Send(this.from,
user.Email,
"Password reset",
"Your reset url: http://mysite.com/?t=" +
user.PasswordResetToken.Value);
}
else
{
this.emailService.Send(this.from,
user.Email,
"Requested password reset",
"Someone requested password reset at http://mysite.com");
}
}
This method violates Single Responsibility Principle - it checks user existence, resets user token, sends emails.
The main issue with such solution is that if I need to add additional steps I have to add implementation for those to RequestPasswordReset method and method becomes more and more complex.
Additional steps could be, for example, to check if user is registered in another related system already and I could advise him create new account in my system or create user account for him.
I was looking at Command pattern - it might be good to refactor service class into separate commands and RequestPasswordReset could be one of those commands. But it doesn't solve main issue with steps inside RequestPasswordReset method.
I was also looking at Chain of Responsibility pattern - it would be good to handle steps in order, but I do not know how it could be used to handle control flow - different conditions which should be implemented. Also it looks like that each handler should do similar actions and it will be not clear how the whole control flow changes.
What would be best approach to refactor such procedural code?

This method violates Single Responsibility Principle
Actually I don't think it does. The "single responsibility" of the service is to reset a user's password, and it does this quite well by coordinating with other objects (user repository and mail service). If the process of resetting a password changes, then this class will change and there is nothing wrong about that.

I think your code is fine as it is. If you absolutely want to refactor something, you might split the code up a little more just to simplify readability of the main method; something like this:
public void RequestPasswordReset(string email)
{
ValidateEmail(email); // May throw exception if invalid
var user = this.repository.FindByEmail(email);
if(user != null)
{
GenerateTokenAndSendPasswordResetEmail(user);
}
else
{
SendPasswordResetEmail(email);
}
}
Other than that, I would leave it as it is. Your problem is really quite simple, so there is no reason to look for a complex solution! :)

The best thing to do with this code is to ensure that it is thoroughly tested. Ensure that all code paths have been tested.
And then, get over it!
Get over the idea that all code has to abide by design patterns, single responsibility principle and other such stuff. Those patterns were discovered by examining working code.
You've got working code here. Get over it and get onto the next task.

Related

Can't get Unity Firebase userID

I'm trying to save the user data to the firebase realtime database directly after the user has been created. But the problem is not the saving, but the UserID. I save also save the user ID that i get from CurrentUser. and then i check in the realtime database and saw that the ID that stored was from a last user who recently created. And i check it in the editor by getting the current user Email and it showed the last user Email not the current user who are creating at the moment. Can someone help me to get the current user ID and not the last user id.
You guys can see the image from the links.
What ID should be
The last user ID showing up instead You guys can see that the ID don't event match. I did try redo the project and looking at the videos that from firebase it self. I really have no ide what to do, i am stuck for 3 days now.
public void SaveNewUserInCode(string userId, string Name, string Email) {
var currentUser = FirebaseAuth.DefaultInstance.CurrentUser;
string userNameId;
if (currentUser != null)
{
userNameId = currentUser.Email;
user = new User(userId, Name, Email);
string Json = JsonUtility.ToJson(user);
reference.Child("Users").Child(currentUser.UserId).SetRawJsonValueAsync(Json);
Data.text = userNameId;
}
}
It would be helpful to see the code that invokes SaveNewUserInCode.
I see a few potential dangers with the code you've posted:
The first is that any call (other than Auth.SignOut) is asynchronous. If you're caching userId immediately after Auth.SignInWithEmailAndPasswordAsync, you'll likely have the previous user still in Auth.CurrentUser (until the related task completes). See my related post on all the ways to wait for a task in Unity if you think this is the issue.
The second, especially if Email is sometimes null, you may be automatically calling Auth.SignInAnonymouslyAsync every time your app starts (perhaps old logic, I usually start my prototypes with anonymous users and later on add real accounts when it's time to do some user testing). This will always overwrite your current user even if you were previously signed in anonymously. You should always check Auth.CurrentUser before calling any of the Auth.SignIn methods, but definitely make sure that you don't have a stray Auth.SignInAnonymouslyAsync laying around.
If the issue is threading, I believe the following logic will fix your problem:
var auth = FirebaseAuth.DefaultInstance;
auth.SignInWithEmailAndPasswordAsync(email, password).ContinueWithOnMainThread(task => {
// omitted : any error handling. Check task's state
var user = task.Result;
SaveNewUserInCode(user.UserId, user.DisplayName /* or however you get Name */, user.Email);
});

Clarifications and peer review regarding authentication and roles of my web application

I am trying to learn basic security and access limitations on ASP MVC.
So far, i have read/watched tutorials but all of them seems different from one another. If i will search something, it will lead me to another implementation which is totally different from what i have.
I implemented Authentication and custom role provider and i have some questions regarding how things work. Majority of explanations that i found from the internet seems overly complicated or outdated.
This is how i implemented my authentication.
login controller:
[HttpGet]
[ActionName("login")]
public ActionResult login_load()
{
return View();
}
[HttpPost]
[ActionName("login")]
public ActionResult login_post(string uname,string pword)
{
using (EmployeeContext emp = new EmployeeContext())
{
int success = emp.login.Where(x => x.username == uname && x.password == pword).Count();
if (success == 1)
{
FormsAuthentication.SetAuthCookie(uname, false);
return RedirectToAction("Details", "Enrollment");
}
return View();
}
}
Then i protected most of my controllers with [Authorize]
Question #1
What's the purpose of FormsAuthentication.SetAuthCookie(uname, false); and what should i typicalfly use it for? would it be alright to store the username. Do i need it for comparison later on?(further security?). It says here that Authentication ticket will be given to the username. Are those the ones with random letters?
--
After that, i decided to dive deeper and implemented a custom role provider
from roleprovider.cs(I only implemented 2 methods so far)
public override string[] GetRolesForUser(string username)
{
if (!HttpContext.Current.User.Identity.IsAuthenticated)
{
return null;
}
var cacheKey = username;
if (HttpRuntime.Cache[cacheKey] != null)
{
return (string[])HttpRuntime.Cache[cacheKey];
}
string[] roles = new string[] { };
using (MvcApplication6.Models.EmployeeContext emp = new MvcApplication6.Models.EmployeeContext())
{
roles = (from a in emp.login
join b in emp.roles on a.role equals b.id
where a.username.Equals(username)
select b.role).ToArray<string>();
if (roles.Count() > 0)
{
HttpRuntime.Cache.Insert(cacheKey, roles, null, DateTime.Now.AddMinutes(_cacheTimeoutInMinute), Cache.NoSlidingExpiration);
}
}
return roles;
}
Question #2
I am kinda confused here and i need a deep clarification: so what is basically the purpose of the cacheKey and from my example, i just made it equal to uname since i have no idea what's going on.
Question #3
Why is it returned (string[])HttpRuntime.Cache[cacheKey]; if the value is null? when is it returned and who is receiving it?
Question #4
After getting the value the list of roles from the database, this function will be called HttpRuntime.Cache.Insert(cacheKey, roles, null, DateTime.Now.AddMinutes(_cacheTimeoutInMinute), Cache.NoSlidingExpiration);. So from what i see, the roles are being inserted into the cache? is it for checking the login type later on?
Question #5
from this lines of code:
public override bool IsUserInRole(string uname, string roleName)
{
var userRoles = GetRolesForUser(uname);
return userRoles.Contains(roleName);
}
When are they exactly triggered and who provides the parameters? is the roleName from the cache?
I am having a hard time visualizing what's happening under the hood. Explanations/Referrals will be very helpful.
What's the purpose of FormsAuthentication.SetAuthCookie()?
This is ASP.NET FormsAuthentication's built-in method for dealing with authentication cookies.
How does cookie based authentication work?
Explained: Forms Authentication in ASP.NET 2.0
Basically, it's doing the hard work for you; creating a cookie for a specific user, giving it to them and then using it to recognise the same user in the future. You want to use this function to log a user in (if they enter correct credentials).
The string parameter is for a username. Yes, you can use username.
The bool parameter is for if you want the cookie to be persistent. That is, keep them logged in even if they close the browser (whether or not to use a session).
By using FormsAuthentication in this way, ASP.NET will automatically detect the user again when they visit subsequent pages.
What is basically the purpose of the cacheKey?
The Cache component of the HttpRuntime is for managing a "box" of objects that you might retrieve frequently but don't want to be hitting the database all the time for.
The Cache is implemented as a kind of Key-Value Pair. The cacheKey in your example is a key in the Key-Value collection. You can think of it like other similar data structures used in other languages.
{
"carlobrew": {
"roles": {
"Name": "Administrator"
}
}
}
So you're basically "saving" the roles of the user carlobrew in a container so that you can get them again later. The key in a Key-Value Pair is used to refer back to the data that you put in there. The key you are using to refer back to the saved information is the uname; that is, the username.
The key in Key-Value Pairs is unique, so you cannot have two keys called carlobrew.
Why is it returned (string[])HttpRuntime.Cache[cacheKey]; if the value is null?
There are two steps to using a typical "cache box" like this.
If we find the key (such as the user carlobrew) then we can simply return the data straight away. It's not if the value is null. It's if the value is not null. That's why the code is if (HttpRuntime.Cache[cacheKey] != null).
If the key cannot be found (that is, we don't have the key for carlobrew), well then we have to add it ourselves, and then return it.
Since it's a cache, ASP.NET MVC will automatically delete things from the cache when the timer expires. That's why you need to check to see if the data is null, and re-create it if it is.
The "who is receiving it" is whichever object is responsible for calling the GetRolesForUser() method in the first place.
So from what i see, the roles are being inserted into the cache?
Yes.
Basically, if the data isn't in the cache, we need to grab it from the database and put it in there ourselves, so we can easily get it back if we call the same method soon.
Let's break it down. We have:
Insert(cacheKey, roles, null, DateTime.Now.AddMinutes(_cacheTimeoutInMinute), Cache.NoSlidingExpiration);
Insert is the method. We're calling this.
cacheKey is the key part of the Key-Value Pair. The username.
roles is the object that we want to store in cache. The object can be anything we want.
DateTime.Now.AddMinutes(_cacheTimeoutInMinute) is telling ASP.NET MVC when we want this data to expire. It can be any amount of time that we want. I'm not sure what the variable _cacheTimeoutInMinute maybe it's 5 or 15 minutes.
Cache.NoSlidingExpiration is a special flag. We're telling ASP.NET that, when we access this data, don't reset the expiration timer back to its full. For example, if our timer was 15 mins, and the timer was about to expire with 1 minute to go, if we were using a sliding expiration and tried to access the data, the timer would reset back to 15 minutes and not expire the data.
Not sure what you mean by "is it for checking the login type later on". But no, there isn't any checking of login type here.
IsUserInRole
You would probably call this when the user is trying to do something. For example, if the user goes to /Admin/Index page, then you could check to see if the user is in the Administrator role. If they aren't, you'd return a 401 Unauthorized response and tell you the user they aren't allowed to access that page.
public Controller Admin
{
public ActionResult Index()
{
if (!IsUserInRole("Administrator"))
{
// redirect "not allowed"
}
return View();
}
}

DotNetOpenAuth: Want to access username while storing Nonce

I am using DotNetOpenAuth 4.0.20926 and trying to implement an OAuth2 based Authentication server with Db Nonce provider.
For some purpose I want to access username in NonceStore's StoreNonce function while processing the GetToken request.
I am not getting a way to retrieve Username in that call.
How can I solve this problem?
Hey Andrew thanks for your reply and DotNetOpenAuth.
My GetToken Method is like this
public ActionResult Token()
{
string userName = "";
//Want to fetch username here
//Using username here
var result = this.authorizationServer.HandleTokenRequest(this.Request);
return result.AsActionResult();
}
And I want to fetch the username before calling HandleTokenRequest.
Is there any Message Parser or Helper method to fetch the username from the request data / Code value .
As you've observed, the interface does not pass the username into the StoreNonce method. So the only way you may possibly be able to get the username would be for you to discover what it is first, before you instantiate your INonceStore instance, and pass it to that nonce store first, so that later when StoreNonce is invoked, it already knows the username.
That said, I believe any design where storing and checking a nonce requires the username needs some rethinking. Not only is it a mixing of concerns that otherwise should remain separate, you may be limiting yourself going forward or even introducing security holes.

Did I Misuse the AntiForgery.Validate Helper from WebMatrix in One of My Pages?

After seeing the AntiForgery.Validate() method error (which it is supposed to do, when the form token does not validate), and visiting this site: http://msdn.microsoft.com/en-us/library/system.web.helpers.antiforgery(v=vs.111).aspx
for more information, it appears that I may have misused this in the past.
I have used the combination of AntiForgery.GetHTML(); and AntiForgery.Validate(); on a login page, but not sure if it makes sense to put it there. Without being logged in, the user is always "" (empty string), so I guess I originally thought that using the AntiForgery class did more than it really does. And, thus, is my question:
Am I right to assume that the only security check that the above practice performs is simply to make sure (by putting AntiForgery.Validate(); in an if(IsPost) branch) that the user who submitted the form is the same user who was logged in when the page was loaded?
If so, then, Am I also right in assuming that utilizing this class in this way has no place in a login page?
If you look at the source code on codeplex, AntiForgery uses AntiForgeryWorker which uses TokenValidator, the ValidateTokens method does do some identity and username checking. If you aren't getting an exception thrown, it might be doing some level of validation but using "" as username.
if (!String.Equals(fieldToken.Username, currentUsername, (useCaseSensitiveUsernameComparison) ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase))
{
throw HttpAntiForgeryException.CreateUsernameMismatchException(fieldToken.Username, currentUsername);
}
However, what is the use case for "forging" a login page? If the forger know the credentials to submit, they could just login themselves. It might not matter if AntiForgery doesn't work to its full potential here.

why does using the same c# code to change password in AD in different computer make different speed?

I used the class in System.DirectoryServices to change password in AD. The code like this:
DirectoryEntry _directoryEntry = new DirectoryEntry(ldapPath, user, pwd, AuthenticationTypes.Secure);
public bool ChangePassword(string userPath, string newPassword)
{
try
{
if (userPath != null && _directoryEntry != null)
{
_directoryEntry.Path = userPath;
//Set the password
_directoryEntry.Invoke("SetPassword", new object[] { newPassword });
_directoryEntry.CommitChanges();
return true;
}
}
catch (Exception ex)
{
//Invalid Login or the domain controller is not contactable
throw ex;
}
finally
{
_directoryEntry.Close();
_directoryEntry = null;
}
return false;
}
I executed these codes on different computer. The time spent from several ms to several seconds.
why does the same code executed in different Environment to change password in AD spent different time? I have spent a lot of time in dealing this problem but still no result. Can anybody tell me? Thank you very much!!!!!
This sounds like it is a simple environment issue. Maybe the network is further away or just slower in general, or it could be that the processor is slower, or just about any number of environmental differences. I would compare some of the key hardware specs. You could also make sure that there are very minimal processes running on each machine to verify that it might not be a conflict from another process.
Well a simple ping report should help you rule out any network related issues. just ping your AD from your different test machines and observe the response time.
You should really get a network trace and see what's going on. There's alot of moving parts here.
That aside, the way this code is laid out is a bit strange. Why are you creating the DirectoryEntry and then changing the Path property?
I had this issue. It is likely because on one computer you are querying the master domain controller directly, and on the other you are querying a read-only domain controller which then has to query the master domain controller. Why? No idea. I just know when I used Wireshark to listen on my AD query traffic, it was always going fast when it was talking to the master domain controller, and slow all the other times. So, I usually include the controller I want in the DirectoryEntry constructor (ie, LDAP://ip-of-controller/cn=whaerver,ou=2whafsal,dc=etc).
You can also open a command prompt and run echo %logonserver% to check which domain controller you computer will default to (I think).

Categories