I am using try
/catch
syntax in combination with a database transaction to (hopefully) prevent partial registrations.
I am wondering if I'm on the right track, and what ways, if any, I can improve my code.
Please be advised that __construct()
is passed an array of unsanitized post values that have been organized into an array after the controller has checked the CSRF tokens, and this object is constructed. Once construction is finished without error, a try catch block on add_user()
. I would show some of the other code, but its a proprietary design pattern that closely emulates angular JavaScript (with far superior performance, but sacrificing readability and longer development time). Someday I'll opensource the design pattern.
Here is an excerpt from my class AddNewUser
that illustrates the question:
function __construct($user)
{
if(!is_array($user)) {
incident('possible hack attempt','registration');
throw new \Exception('Invalid data received');
}
if($user['termsofservice'] !== 'agree') {
incident('non ajax submission','registration');
throw new \Exception ('Must agree to terms of service');
}
if($user['privacypolicy'] !== 'agree') {
incident('non ajax submission','registration');
throw new \Exception ('Must agree to privacy policy');
}
if(usernameExists($user['username'])){
incident('possible user enumeration','registration');
throw new \Exception('Username Taken');
}
if(emailExists($user['email'])) {
incident('possible email enumeration','registration');
throw new \Exception('Email Taken');
}
if(minMaxRange(3,25,$user['username'])) {
throw new \Exception('Username must be 3 to 25 charachters in length') ;
}
if(minMaxRange(8,50,$user['password'])) {
throw new \Exception('Password must be 8 to 50 charachters in length');
}
$this->user_name = security($user['username']);
$this->user_pass = generateHash($user['password']);
$this->user_email = $user['email'];
$this->user_ip = get_ip_address();
$this->verification = generateActivationToken();
$this->signupstamp = time();
$this->user_agent = security($user['agent']);
$this->user_active = 0;
$this->user_verified = 0;
$this->terms_of_service = security($user['termsofservice']);
$this->privacy_policy = security($user['privacypolicy']);
}
public function adduser()
{
global $db,$cfg;
try{
$query = $db->query("START TRANSACTION;");
$stmt = $db->prepare("INSERT INTO users (u_name, u_pass, u_email, u_verified, u_ip, u_active, u_verification, u_signup_stamp, u_agent) VALUES (?,?,?,?,?,?,?,?,?)");
$stmt->bind_param('sssisisis',$this->user_name,$this->user_pass,$this->user_email,$this->user_verified,$this->user_ip,$this->user_active,$this->verification,$this->signupstamp,$this->user_agent);
$stmt->execute();
$stmt->close();
$stmt = $db->prepare("INSERT INTO termsofservice (ip_addr,u_name,u_agent,answer,timestamp) VALUES (?,?,?,?,?)");
$stmt->bind_param('ssssi',$this->user_ip,$this->user_name,$this->user_agent,$this->terms_of_service,$this->signupstamp);
$stmt->execute();
$stmt->close();
$stmt = $db->prepare("INSERT INTO privacypolicy (ip_addr,u_name,u_agent,answer,timestamp) VALUES (?,?,?,?,?)");
$stmt->bind_param('ssssi',$this->user_ip,$this->user_name,$this->user_agent,$this->privacy_policy,$this->signupstamp);
$stmt->execute();
$stmt->close();
try{
$mail = new Email('registration',$this->user_email,$this->user_name,'Important: Account Activation',$cfg['email']['no-reply']);
$query = $db->query("COMMIT;");
}
catch(Exception $e){
incident('smtp unreachable','registration');
throw new \Exception('Unable to send activation email');
}
return true;
}
catch(Exception $e) {
incident('sql problems','registration');
throw new \Exception('An unknown Error Occured');
}
}