22
33namespace BNETDocs \Controllers \User ;
44
5+ use \BNETDocs \Libraries \CSRF ;
56use \BNETDocs \Libraries \Common ;
67use \BNETDocs \Libraries \Controller ;
8+ use \BNETDocs \Libraries \Exceptions \RecaptchaException ;
79use \BNETDocs \Libraries \Exceptions \UnspecifiedViewException ;
10+ use \BNETDocs \Libraries \Exceptions \UserNotFoundException ;
11+ use \BNETDocs \Libraries \Exceptions \QueryException ;
12+ use \BNETDocs \Libraries \Logger ;
813use \BNETDocs \Libraries \Router ;
14+ use \BNETDocs \Libraries \User ;
915use \BNETDocs \Libraries \UserSession ;
1016use \BNETDocs \Models \User \Register as UserRegisterModel ;
1117use \BNETDocs \Views \User \RegisterHtml as UserRegisterHtmlView ;
@@ -21,7 +27,14 @@ public function run(Router &$router) {
2127 throw new UnspecifiedViewException ();
2228 }
2329 $ model = new UserRegisterModel ();
30+ $ model ->csrf_id = mt_rand ();
31+ $ model ->csrf_token = CSRF ::generate ($ model ->csrf_id );
32+ $ model ->captcha_key = Common::$ config ->recaptcha ->sitekey ;
33+ $ model ->error = null ;
2434 $ model ->user_session = UserSession::load ($ router );
35+ if ($ router ->getRequestMethod () == "POST " ) {
36+ $ this ->tryRegister ($ router , $ model );
37+ }
2538 ob_start ();
2639 $ view ->render ($ model );
2740 $ router ->setResponseCode (200 );
@@ -31,4 +44,134 @@ public function run(Router &$router) {
3144 ob_end_clean ();
3245 }
3346
47+ protected function tryRegister (Router &$ router , UserRegisterModel &$ model ) {
48+ $ data = $ router ->getRequestBodyArray ();
49+ $ model ->email = (isset ($ data ["email " ]) ? $ data ["email " ] : null );
50+ $ model ->username = (isset ($ data ["username " ]) ? $ data ["username " ] : null );
51+ if (isset ($ model ->user_session )) {
52+ $ model ->error = "ALREADY_LOGGED_IN " ;
53+ return ;
54+ }
55+ $ csrf_id = (isset ($ data ["csrf_id " ]) ? $ data ["csrf_id " ] : null );
56+ $ csrf_token = (isset ($ data ["csrf_token " ]) ? $ data ["csrf_token " ] : null );
57+ $ csrf_valid = CSRF ::validate ($ csrf_id , $ csrf_token );
58+ if (!$ csrf_valid ) {
59+ $ model ->error = "INVALID_CSRF " ;
60+ return ;
61+ }
62+ CSRF ::invalidate ($ csrf_id );
63+ $ email = $ model ->email ;
64+ $ username = $ model ->username ;
65+ $ pw1 = (isset ($ data ["pw1 " ]) ? $ data ["pw1 " ] : null );
66+ $ pw2 = (isset ($ data ["pw2 " ]) ? $ data ["pw2 " ] : null );
67+ $ captcha = (
68+ isset ($ data ["g-recaptcha-response " ]) ?
69+ $ data ["g-recaptcha-response " ] :
70+ null
71+ );
72+ if (!self ::verifyCaptcha ($ captcha )) {
73+ $ model ->error = "INVALID_CAPTCHA " ;
74+ return ;
75+ }
76+ if ($ pw1 !== $ pw2 ) {
77+ $ model ->error = "NONMATCHING_PASSWORD " ;
78+ return ;
79+ }
80+ $ pwlen = strlen ($ pw1 );
81+ $ usernamelen = strlen ($ username );
82+ $ req = Common::$ config ->bnetdocs ->user_register_requirements ;
83+ if ($ req ->email_validate_quick
84+ && !filter_var ($ email , FILTER_VALIDATE_EMAIL )) {
85+ $ model ->error = "INVALID_EMAIL " ;
86+ return ;
87+ }
88+ if (!$ req ->password_allow_email && stripos ($ pw1 , $ email )) {
89+ $ model ->error = "PASSWORD_CONTAINS_EMAIL " ;
90+ return ;
91+ }
92+ if (!$ req ->password_allow_username && stripos ($ pw1 , $ username )) {
93+ $ model ->error = "PASSWORD_CONTAINS_USERNAME " ;
94+ return ;
95+ }
96+ if (is_numeric ($ req ->username_length_max )
97+ && $ usernamelen > $ req ->username_length_max ) {
98+ $ model ->error = "USERNAME_TOO_LONG " ;
99+ return ;
100+ }
101+ if (is_numeric ($ req ->username_length_min )
102+ && $ usernamelen < $ req ->username_length_min ) {
103+ $ model ->error = "USERNAME_TOO_SHORT " ;
104+ return ;
105+ }
106+ if (is_numeric ($ req ->password_length_max )
107+ && $ pwlen > $ req ->password_length_max ) {
108+ $ model ->error = "PASSWORD_TOO_LONG " ;
109+ return ;
110+ }
111+ if (is_numeric ($ req ->password_length_min )
112+ && $ pwlen < $ req ->password_length_min ) {
113+ $ model ->error = "PASSWORD_TOO_SHORT " ;
114+ return ;
115+ }
116+ if (Common::$ config ->bnetdocs ->user_register_disabled ) {
117+ $ model ->error = "REGISTER_DISABLED " ;
118+ return ;
119+ }
120+ try {
121+ if (!$ req ->email_duplicate_allowed && User::findIdByEmail ($ email )) {
122+ $ model ->error = "EMAIL_ALREADY_USED " ;
123+ return ;
124+ }
125+ } catch (UserNotFoundException $ e ) {}
126+ try {
127+ $ success = User::create ($ email , $ username , null , $ pw1 , 0 );
128+ } catch (QueryException $ e ) {
129+ // SQL error occurred. We can show a friendly message to the user while
130+ // also notifying this problem to staff.
131+ Logger::logException ($ e );
132+ }
133+ if (!$ success ) {
134+ $ model ->error = "INTERNAL_ERROR " ;
135+ } else {
136+ $ model ->error = false ;
137+ }
138+ Logger::logEvent (
139+ "user_created " ,
140+ null ,
141+ getenv ("REMOTE_ADDR " ),
142+ json_encode ([
143+ "error " => $ model ->error ,
144+ "requirements " => $ req ,
145+ "email " => $ email ,
146+ "username " => $ username ,
147+ "display_name " => null ,
148+ "options_bitmask " => 0 ,
149+ ])
150+ );
151+ }
152+
153+ protected static function verifyCaptcha ($ g_captcha_response ) {
154+ $ data = [
155+ "secret " => Common::$ config ->recaptcha ->secret ,
156+ "response " => $ g_captcha_response ,
157+ "remoteip " => getenv ("REMOTE_ADDR " ),
158+ ];
159+ $ r = Common::curlRequest (Common::$ config ->recaptcha ->url , $ data );
160+ Logger::logMetric ("response_code " , $ r ->code );
161+ Logger::logMetric ("response_type " , $ r ->type );
162+ Logger::logMetric ("response_data " , $ r ->data );
163+ if ($ r ->code != 200 )
164+ throw new RecaptchaException ("Received bad HTTP status " );
165+ if (stripos ($ r ->type , "json " ) === false )
166+ throw new RecaptchaException ("Received unknown content type " );
167+ if (empty ($ data ))
168+ throw new RecaptchaException ("Received empty response " );
169+ $ j = json_decode ($ r ->data );
170+ $ e = json_last_error ();
171+ Logger::logMetric ("json_last_error " , $ e );
172+ if (!$ j || $ e !== JSON_ERROR_NONE || !property_exists ($ j , "success " ))
173+ throw new RecaptchaException ("Received invalid response " );
174+ return ($ j ->success );
175+ }
176+
34177}
0 commit comments