I am writing a user account system in PHP with an emphasis on security, but I'm stuck with reorganizing it into something cleaner and more convenient.
The problem is combining the functions of the user account together, but in separate classes. The way I'm doing it now is a bunch of classes with public static methods that all take $ username as the first parameter, and they use other static methods, passing the same $ username as the first parameter. Obviously, OOP is the best way to go, especially since each method must strtolower the username in order to query the database, and must handle the case when the provided username does not exist at all.
The problem with putting everything in the "User" class is that it will be huge, and there will be a lot of completely unrelated code in one file. For example, the Change password code does not require calling methods related to checking the user's email or changing user (unrelated) settings.
I could have two classes: User and AuthenticatedUser, where AuthenticatedUser inherits the user. The user will implement all the functions that are possible without the user entering the system, and AuthenticatedUser - these are all functions that require entering the system - for example, access to encrypted user data. The service code can use user objects, and the GUI code will receive an AuthenticatedUser object after the user logs in. But I do not want to embed all functions in User.
Here is a list of some of the operations associated with a user account to show why they will not be in the same class:
- To come in
- Block the user if> = X attempts in the last Y minutes (includes methods for determining whether the user is blocked, adding a checkmark to the number of attempts, etc.).
- Email Bypass Blocking
- Change password
- Administrator Change Password (Strength)
- Password reset (email loop) (including methods for starting reset, checking email token, etc.)
- Set / get user data stored in clear text
- Set / Get Encrypted User Data
- Set / receive account settings (is the reset password allowed ?, block the account in case of password failures ?, bypasses the lock using the email cycle?) Etc.) Ideally, these settings should be set / installed next to the code, behavior which depends on them.
- Get the username of the user in the appropriate case (since they indicated it when creating the account)
- Email Authentication
- A lot of functionality used only by a certain code. For instance. get the user's salt used for key derivation.
- More details ...
I thought I could do something like the PasswordChanger class, which inherits the user, and implements password change functions. So it will look like this:
$proper = $user->getProperName(); ... $passChanger = $user->getPasswordChanger(); $result = $passChanger->changePassword($oldPass, $newPass); ... $userLockout = $user->getUserLockout(); $result = $userLockout->isUserLockedOut(); ... $userEV = $user->getEmailValidation(); $result = $userEV->tryValidateEmail($token); ...
This is the best solution that I have come up with so far. This allows me to split related functions into my own file and get rid of the need to pass in a username. But it seems really strange - I have never seen such code before. This makes the superclass aware of all its subclasses, which is poorly designed. Any ideas?
Edit: An alternative to avoiding inheritance would be to have a PasswordChanger that has a User. How:
$passChanger = new PasswordChanger($user); $passChanger->changePassword($old, $new); // uses $user->getUsername() ... $emailValidator = new EmailValdiator($user); $emailValidator->tryValidate($token);
"PasswordChanger has a user" and "PasswordChanger is a user." The first one actually makes sense, so I like it a little better.