2 package SGN
::Controller
::AJAX
::User
;
8 BEGIN { extends
'Catalyst::Controller::REST' };
11 default => 'application/json',
13 map => { 'application/json' => 'JSON', 'text/html' => 'JSON' },
17 sub login
: Path
('/ajax/user/login') Args
(0) {
21 my $username = $c->req->param("username");
22 my $password = $c->req->param("password");
23 my $goto_url = $c->req->param("goto_url");
25 print STDERR
"Goto URL = $goto_url\n";
27 my $login = CXGN
::Login
->new($c->dbc->dbh());
28 my $login_info = $login->login_user($username, $password);
30 if (exists($login_info->{incorrect_password
}) && $login_info->{incorrect_password
} == 1) {
31 $c->stash->{rest
} = { error
=> "Login credentials are incorrect. Please try again." };
34 elsif (exists($login_info->{account_disabled
}) && $login_info->{account_disabled
}) {
35 $c->stash->{rest
} = { error
=> "This account has been disabled due to $login_info->{account_disabled}. Please contact the database to fix this problem." };
40 message
=> "Login successful",
46 sub logout
:Path
('/ajax/user/logout') Args
(0) {
50 my $login = CXGN
::Login
->new($c->dbc->dbh());
51 $login->logout_user();
53 $c->stash->{rest
} = { message
=> "User successfully logged out." };
56 sub new_account
:Path
('/ajax/user/new') Args
(0) {
60 print STDERR
"Adding new account...\n";
61 if ($c->config->{is_mirror
}) {
62 $c->stash->{template
} = '/system_message.mas';
63 $c->stash->{message
} = "This site is a mirror site and does not support adding users. Please go to the main site to create an account.";
68 my ($first_name, $last_name, $username, $password, $confirm_password, $email_address, $organization)
69 = map { $c->req->params->{$_} } (qw
|first_name last_name username password confirm_password email_address organization
|);
73 # check password properties...
76 if (length($username) < 7) {
77 push @fail, "Username is too short. Username must be 7 or more characters";
79 # does user already exist?
81 my $existing_login = CXGN
::People
::Login
-> get_login
($c->dbc()->dbh(), $username);
83 if ($existing_login->get_username()) {
84 push @fail, "Username \"$username\" is already in use. Please pick a different username.";
88 if (length($password) < 7) {
89 push @fail, "Password is too short. Password must be 7 or more characters";
91 if ("$password" ne "$confirm_password") {
92 push @fail, "Password and confirm password do not match.";
96 push @fail, "'Organization' is required.'";
99 if ($password eq $username) {
100 push @fail, "Password must not be the same as your username.";
102 if ($email_address !~ m/[^\@]+\@[^\@]+/) {
103 push @fail, "Email address is invalid.";
105 unless($first_name) {
106 push @fail,"You must enter a first name or initial.";
109 push @fail,"You must enter a last name.";
113 $c->stash->{rest
} = { error
=> "Account creation failed for the following reason(s): ".(join ", ", @fail) };
118 my $confirm_code = $self->tempname();
119 my $new_user = CXGN
::People
::Login
->new($c->dbc->dbh());
120 $new_user -> set_username
($username);
121 $new_user -> set_pending_email
($email_address);
122 $new_user -> set_disabled
('unconfirmed account');
123 $new_user -> set_organization
($organization);
124 $new_user -> store
();
126 print STDERR
"Generated sp_person_id ".$new_user->get_sp_person_id()."\n";
127 print STDERR
"Update password and confirm code...\n";
128 $new_user->update_password($password);
129 $new_user->update_confirm_code($confirm_code);
131 print STDERR
"Store Person object...\n";
132 #this is being added because the person object still uses two different objects, despite the fact that we've merged the tables
133 my $person_id=$new_user->get_sp_person_id();
134 my $new_person=CXGN
::People
::Person
->new($c->dbc->dbh(),$person_id);
135 $new_person->set_first_name($first_name);
136 $new_person->set_last_name($last_name);
137 $new_person->store();
139 my $host = $c->config->{main_production_site_url
};
140 my $subject="[SGN] Email Address Confirmation Request";
141 my $body=<<END_HEREDOC;
143 Please do *NOT* reply to this message. The return address is not valid.
144 Use the <a href="/contact/form">contact form</a> instead.
146 This message is sent to confirm the email address for community user
149 Please click (or cut and paste into your browser) the following link to
150 confirm your account and email address:
152 $host/solpeople/account-confirm.pl?username=$username&confirm=$confirm_code
159 CXGN::Contact::send_email($subject,$body,$email_address);
160 $c->stash->{rest} = { message => qq | <table summary="" width="80%" align="center">
161 <tr><td><p>Account was created with username \"$username\". To continue, you must confirm that SGN staff can reach you via email address \"$email_address\". An email has been sent with a URL to confirm this address. Please check your email for this message and use the link to confirm your email address.</p></td></tr>
162 <tr><td><br /></td></tr>
168 sub change_account_info_action :Path('/ajax/user/update') Args(0) {
173 $c->stash->{rest} = { error => "You must be logged in to use this page." };
177 my $person = new CXGN::People::Login($c->dbc->dbh(), $c->user->get_sp_person_id());
179 # my ($current_password, $change_username, $change_password, $change_email) = $c->req->param({qw(current_password change_username change_password change_email)});
181 my $args = $c->req->params();
183 if (!$args->{change_password
} && ! $args->{change_username
} && !$args->{change_email
}) {
184 my $error = "No actions were requested. Please select which fields you would like to update by checking the appropriate checkbox(es) on the form and entering your new information.";
186 $c->stash->{rest
} = { error
=> $error };
190 chomp($args->{current_password
});
191 if (! $person->verify_password($args->{current_password
})) {
192 my $error = "Your current password does not match SGN records.";
194 $c->stash->{rest
} = { error
=> "$error" };
198 # Check for error conditions in all changes, before making any of them.
199 # Otherwise, we could end up making some changes and then failing on later
200 # ones. The user would then push the back button and their information may
201 # be different now but they will probably assume no changes were made. This
202 # is most troublesome if the current password changes.
204 if ($args->{change_username
}) {
205 #unless change_username is set, new_username won't be in the args hash because of the prestore test
206 my $new_username = $args->{new_username
};
207 if(length($new_username) < 7) {
208 my $error = "Username must be at least 7 characters long.";
210 $c->stash->{rest
} = { error
=> $error };
214 my $other_user = CXGN
::People
::Login
->get_login($c->dbc->dbh(), $new_username);
215 if (defined $other_user->get_sp_person_id() &&
216 ($person -> get_sp_person_id
() != $other_user->get_sp_person_id())) {
217 print STDERR
"Username alread in use.\n";
218 $c->stash->{rest
} = { error
=> "Username \"$new_username\" is already in use. Please select a different username." };
222 $person->set_username($new_username);
226 if ($args->{change_password
}) {
227 #unless change_password is set, new_password won't be in the args hash because of the prestore test
228 my ($new_password, $confirm_password) = ($args->{new_password
}, $args->{confirm_password
});
229 if(length($args->{new_password
}) < 7) {
230 print STDERR
"Password too short\n";
231 $c->stash->{rest
} = { error
=> "Passwords must be at least 7 characters long. Please try again." };
235 if($args->{new_password
} !~ /^[a-zA-Z0-9~!@#$^&*_.=:;<>?]+$/) {
236 print STDERR
"Illegal characters in password\n";
237 $c->stash->{rest
} = { error
=> "An error occurred. Please use your browser's back button to try again.. The Password can't contain spaces or these symbols: <u><b>` ( ) [ ] { } - + ' \" / \\ , |</b></u>." };
240 if($args->{new_password
} ne $args->{confirm_password
}) {
241 print STDERR
"Password don't match.\n";
242 $c->stash->{rest
} = { error
=> "New password entries do not match. You must enter your new password twice to verify accuracy." };
246 print STDERR
"Saving new password to the database\n";
247 $person->update_password($args->{new_password
});
250 my $user_private_email = $c->user->get_private_email();
251 if($args->{change_email
}) {
252 #unless change_email is set, private_email won't be in the args hash because of the prestore test
253 my ($private_email, $confirm_email) = ($args->{private_email
}, $args->{confirm_email
});
254 if($private_email !~ m/^[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.-]+$/) {
255 print STDERR
"Invalid email address\n";
256 $c->stash->{rest
} = { error
=> "An error occurred. Please use your browser's back button to try again. The E-mail address \"$private_email\" does not appear to be a valid e-mail address." };
259 if($private_email ne $confirm_email) {
260 print STDERR
"Emails don't match\n";
261 $c->stash->{rest
} = { error
=> "An error occurred. Please use your browser's back button to try again. New e-mail address entries do not match. You must enter your new e-mail address twice to verify accuracy." };
265 print STDERR
"Saving private email '$private_email' to the database\n";
266 $person->set_private_email($private_email);
267 my $confirm_code = $self->tempname();
268 $person->set_confirm_code($confirm_code);
271 $user_private_email = $private_email;
273 $self->send_confirmation_email($args->{username
}, $user_private_email, $confirm_code, $c->config->{main_production_site_url
});
277 $c->stash->{rest
} = { message
=> "Update successful" };
281 sub send_confirmation_email
{
282 my ($self, $username, $private_email, $confirm_code, $host) = @_;
283 my $subject = "[SGN] E-mail Address Confirmation Request";
285 my $body = <<END_HEREDOC;
287 You requested an account on the site $host.
289 Please do *NOT* reply to this message. The return address is not valid.
290 Use the contact form at $host/contact/form instead.
292 This message is sent to confirm the private e-mail address for community user
295 Please click (or cut and paste into your browser) the following link to
296 confirm your account and e-mail address:
298 $host/user/confirm?username=$username&confirm=$confirm_code
304 CXGN::Contact::send_email($subject, $body, $private_email);
307 sub reset_password :Path('/ajax/user/reset_password') Args(0) {
311 my $email = $c->req->param('password_reset_email');
313 my @person_ids = CXGN::People::Login->get_login_by_email($c->dbc->dbh(), $email);
316 $c->stash->{rest} = { error => "The provided email ($email) is not associated with any account." };
320 if (@person_ids > 1) {
321 $c->stash->{rest} = { message => "The provided email ($email) is associated with multiple accounts. An email is sent for each account. Please notify the database team using the contact form to consolidate the accounts." };
325 foreach my $pid (@person_ids) {
326 my $email_reset_token = $self->tempname();
327 $reset_link = $c->config->{main_production_site_url}."/user/reset_password_form?reset_password_token=$email_reset_token";
328 my $person = CXGN::People::Login->new( $c->dbc->dbh(), $pid);
329 $person->update_confirm_code($email_reset_token);
330 print STDERR "Sending reset link $reset_link\n";
331 $self->send_reset_email_message($c, $pid, $email, $reset_link);
334 $c->stash->{rest} = { message => "Reset link sent. Please check your email and click on the link." };
337 sub process_reset_password_form :Path('/ajax/user/process_reset_password') Args(0) {
341 my $token = $c->req->param("token");
342 my $new_password = $c->req->param("");
345 my $sp_person_id = CXGN::People::Login->get_login_by_token($c->dbc->dbh, $token);
347 my $login = CXGN::People::Login->new($c->dbc->dbh(), $sp_person_id);
348 $login->update_password($new_password);
349 $login->update_confirm_code("");
352 $c->stash->{rest} = { error => $@ };
355 $c->stash->{rest} = { message => "The password was successfully updated." };
361 sub send_reset_email_message {
365 my $private_email = shift;
366 my $reset_link = shift;
368 my $subject = "[SGN] E-mail Address Confirmation Request";
369 my $main_url = $c->config->{main_production_site_url};
371 my $body = <<END_HEREDOC
;
375 you have requested a password
reset on
$main_url.
377 If this request did
not come from you
, please let us know
.
379 To contact us
, please
do NOT reply to this message
; rather
, use the contact form
($main_url/contact/form
) instead
.
381 Your password can be
reset using the following
link, which you can either click
or cut
and paste into your browser
:
387 Your friends at
$main_url
391 CXGN
::Contact
::send_email
($subject, $body, $private_email);
396 my $rand_string = "";
397 my $dev_urandom = new IO
::File
"</dev/urandom" || print STDERR
"Can't open /dev/urandom";
398 $dev_urandom->read( $rand_string, 16 );
399 my @bytes = unpack( "C16", $rand_string );
404 $rand_string .= chr( 65 + $_ );
407 $rand_string .= chr( 97 + ( $_ - 26 ) );
410 $rand_string .= chr( 48 + ( $_ - 52 ) );
416 sub get_login_button_html
:Path
('/ajax/user/login_button_html') Args
(0) {
420 my $production_site = $c->config->{main_production_site_url
};
422 # if the site is a mirror, gray out the login/logout links
423 if( $c->config->{'is_mirror'} ) {
424 print STDERR
"generating login button for mirror site...\n";
426 <a style="line-height: 1.2; text-decoration: underline; background: none" href="$production_site" title="log in on main site">main site</a>
427 } elsif ( $c->config->{disable_login} ) {
428 <li class="dropdown">
429 <div class="btn-group" role="group" aria-label="..." style="height:34px; margin: 1px 0px 0px 0px" >
430 <button class="btn btn-primary disabled" type="button" style="margin: 7px 7px 0px 0px">Login</button>
436 } elsif ( $c->req->uri->path_query =~ "logout=yes") {
437 print STDERR
"generating login button for logout...\n";
439 <li class="dropdown">
440 <div class="btn-group" role="group" aria-label="..." style="height:34px; margin: 1px 0px 0px 0px" >
441 <a href="/user/login">
442 <button class="btn btn-primary" type="button" style="margin: 7px 7px 0px 0px">Login</button>
448 } elsif ( $c->user_exists ) {
449 print STDERR
"Generate login button for logged in user...\n";
450 my $sp_person_id = $c->user->get_object->get_sp_person_id;
451 my $username = $c->user->get_username();
454 <div class="btn-group" role="group" aria-label="..." style="height:34px; margin: 1px 3px 0px 0px">
455 <button id="navbar_profile" class="btn btn-primary" type="button" onclick='location.href="/solpeople/profile/$sp_person_id"' style="margin: 7px 0px 0px 0px" title="My Profile">$username</button>
456 <button id="navbar_lists" name="lists_link" class="btn btn-info" style="margin:7px 0px 0px 0px" type="button" title="Lists" onClick="show_lists();">
457 Lists <span class="glyphicon glyphicon-list-alt" ></span>
459 <button id="navbar_personal_calendar" name="personal_calendar_link" class="btn btn-primary" style="margin:7px 0px 0px 0px" type="button" title="Your Calendar">Calendar <span class="glyphicon glyphicon-calendar" ></span>
461 <button id="navbar_logout" class="btn btn-default glyphicon glyphicon-log-out" style="margin:6px 0px 0px 0px" type="button" onclick="logout();" title="Logout"></button>
467 print STDERR
"generating regular login button..\n";
469 <li
class="dropdown">
470 <div
class="btn-group" role
="group" aria
-label
="..." style
="height:34px; margin: 1px 0px 0px 0px" >
471 <button id
="site_login_button" name
="site_login_button" class="btn btn-primary" type
="button" style
="margin: 7px 7px 0px 0px; position-absolute: 10,10,100,10">Login
</button
>
478 print STDERR
"ERROR: $@\n";
479 $c->stash->{rest
} = { error
=> $@
};
481 return $c->stash->{rest
} = { html
=> $html };