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");
24 my $login = CXGN
::Login
->new($c->dbc->dbh());
25 my $login_info = $login->login_user($username, $password);
27 if (exists($login_info->{incorrect_password
}) && $login_info->{incorrect_password
} == 1) {
28 $c->stash->{rest
} = { error
=> "Login credentials are incorrect. Please try again." };
31 elsif (exists($login_info->{account_disabled
}) && $login_info->{account_disabled
}) {
32 $c->stash->{rest
} = { error
=> "This account has been disabled due to $login_info->{account_disabled}. Please contact the database to fix this problem." };
36 $c->stash->{rest
} = { message
=> 'Something happened, but nodoby knows what.' };
38 $c->stash->{rest
} = { message
=> "Login successful" };
43 sub new_account
:Path
('/ajax/user/new') Args
(0) {
47 if ($c->conf->{is_mirror
}) {
48 $c->stash->{template
} = '/system_message.mas';
49 $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.";
54 my ($first_name, $last_name, $username, $password, $confirm_password, $email_address, $organization)
55 = $c->req->param(qw(first_name last_name username password confirm_password email_address organization));
59 # check password properties...
62 if (length($username) < 7) {
63 push @fail, "Username is too short. Username must be 7 or more characters";
65 # does user already exist?
67 my $existing_login = CXGN
::People
::Login
-> get_login
($c->dbc()->dbh(), $username);
69 if ($existing_login->get_username()) {
70 push @fail, "Username \"$username\" is already in use. Please pick a different username.";
74 if (length($password) < 7) {
75 push @fail, "Password is too short. Password must be 7 or more characters";
77 if ("$password" ne "$confirm_password") {
78 push @fail, "Password and confirm password do not match.";
82 push @fail, "'Organization' is required.'";
85 if ($password eq $username) {
86 push @fail, "Password must not be the same as your username.";
88 if ($email_address !~ m/[^\@]+\@[^\@]+/) {
89 push @fail, "Email address is invalid.";
92 push @fail,"You must enter a first name or initial.";
95 push @fail,"You must enter a last name.";
99 $c->stash->{rest
} = { error
=> "Account creation failed for the following reason(s): ".(join ", ", @fail) };
104 my $confirm_code = $self->tempname();
105 my $new_user = CXGN
::People
::Login
->new($c->dbc->dbh());
106 $new_user -> set_username
($username);
107 $new_user -> set_password
($password);
108 $new_user -> set_pending_email
($email_address);
109 $new_user -> set_confirm_code
($confirm_code);
110 $new_user -> set_disabled
('unconfirmed account');
111 $new_user -> set_organization
($organization);
112 $new_user -> store
();
114 #this is being added because the person object still uses two different objects, despite the fact that we've merged the tables
115 my $person_id=$new_user->get_sp_person_id();
116 my $new_person=CXGN
::People
::Person
->new($self->dbc->dbh(),$person_id);
117 $new_person->set_first_name($first_name);
118 $new_person->set_last_name($last_name);
119 $new_person->store();
121 my $host = $c->req()->hostname();
122 my $subject="[SGN] Email Address Confirmation Request";
123 my $body=<<END_HEREDOC;
125 Please do *NOT* reply to this message. The return address is not valid.
126 Use the <a href="/contact/form">contact form</a> instead.
128 This message is sent to confirm the email address for community user
131 Please click (or cut and paste into your browser) the following link to
132 confirm your account and email address:
134 https://$host/solpeople/account-confirm.pl?username=$username&confirm=$confirm_code
141 CXGN::Contact::send_email($subject,$body,$email_address);
142 $c->stash->{rest} = { message => qq | <table summary="" width="80%" align="center">
143 <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>
144 <tr><td><br /></td></tr>
150 sub change_account_info_action :Path('/ajax/user/update') Args(0) {
155 $c->stash->{rest} = { error => "You must be logged in to use this page." };
159 my $person = new CXGN::People::Login($c->dbc->dbh(), $c->user->get_sp_person_id());
161 # my ($current_password, $change_username, $change_password, $change_email) = $c->req->param({qw(current_password change_username change_password change_email)});
163 my $args = $c->req->params();
165 if (!$args->{change_password
} && ! $args->{change_username
} && !$args->{change_email
}) {
166 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.";
168 $c->stash->{rest
} = { error
=> $error };
172 print STDERR
"Person = ".$person->get_username()."\n";
173 chomp($args->{current_password
});
174 if (! $person->verify_password($args->{current_password
})) {
175 my $error = "Your current password does not match SGN records.";
177 $c->stash->{rest
} = { error
=> "$error" };
181 # Check for error conditions in all changes, before making any of them.
182 # Otherwise, we could end up making some changes and then failing on later
183 # ones. The user would then push the back button and their information may
184 # be different now but they will probably assume no changes were made. This
185 # is most troublesome if the current password changes.
187 if ($args->{change_username
}) {
188 #unless change_username is set, new_username won't be in the args hash because of the prestore test
189 my $new_username = $args->{new_username
};
190 if(length($new_username) < 7) {
191 my $error = "Username must be at least 7 characters long.";
193 $c->stash->{rest
} = { error
=> $error };
197 my $other_user = CXGN
::People
::Login
->get_login($c->dbc->dbh(), $new_username);
198 if (defined $other_user->get_sp_person_id() &&
199 ($person -> get_sp_person_id
() != $other_user->get_sp_person_id())) {
200 print STDERR
"Username alread in use.\n";
201 $c->stash->{rest
} = { error
=> "Username \"$new_username\" is already in use. Please select a different username." };
205 print STDERR
"Saving new username args->{username} to the database...\n";
206 $person->set_username($new_username);
210 if ($args->{change_password
}) {
211 #unless change_password is set, new_password won't be in the args hash because of the prestore test
212 my ($new_password, $confirm_password) = ($args->{new_password
}, $args->{confirm_password
});
213 if(length($args->{new_password
}) < 7) {
214 print STDERR
"Password too short\n";
215 $c->stash->{rest
} = { error
=> "Passwords must be at least 7 characters long. Please try again." };
219 if($args->{new_password
} !~ /^[a-zA-Z0-9~!@#$^&*_.=:;<>?]+$/) {
220 print STDERR
"Illegal characters in password\n";
221 $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>." };
224 if($args->{new_password
} ne $args->{confirm_password
}) {
225 print STDERR
"Password don't match.\n";
226 $c->stash->{rest
} = { error
=> "New password entries do not match. You must enter your new password twice to verify accuracy." };
230 print STDERR
"Saving new password '$args->{new_password}' to the database\n";
231 $person->update_password($args->{new_password
});
234 my $user_private_email = $c->user->get_private_email();
235 if($args->{change_email
}) {
236 #unless change_email is set, private_email won't be in the args hash because of the prestore test
237 my ($private_email, $confirm_email) = ($args->{private_email
}, $args->{confirm_email
});
238 if($private_email !~ m/^[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.-]+$/) {
239 print STDERR
"Invalid email address\n";
240 $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." };
243 if($private_email ne $confirm_email) {
244 print STDERR
"Emails don't match\n";
245 $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." };
249 print STDERR
"Saving private email '$private_email' to the database\n";
250 $person->set_private_email($private_email);
251 my $confirm_code = $self->tempname();
252 $person->set_confirm_code($confirm_code);
255 $user_private_email = $private_email;
257 $self->send_confirmation_email($args->{username
}, $user_private_email, $confirm_code, $c->req->hostname());
261 $c->stash->{rest
} = { message
=> "Update successful" };
265 sub send_confirmation_email
{
266 my ($self, $username, $private_email, $confirm_code, $host) = @_;
267 my $subject = "[SGN] E-mail Address Confirmation Request";
268 my $body = <<END_HEREDOC;
269 Please do *NOT* reply to this message. The return address is not valid.
270 Use <a href="/contact/form">the contact form</a> instead.
272 This message is sent to confirm the private e-mail address for community user
275 Please click (or cut and paste into your browser) the following link to
276 confirm your account and e-mail address:
278 http://$host/user/confirm?username=$username&confirm=$confirm_code
284 CXGN::Contact::send_email($subject, $body, $private_email);
287 sub reset_password :Path('/ajax/user/reset_password') Args(0) {
291 my $email = $c->req->param('password_reset_email');
293 my @person_ids = CXGN::People::Login->get_login_by_email($c->dbc->dbh(), $email);
295 print STDERR Dumper(\@person_ids);
297 $c->stash->{rest} = { error => "The provided email ($email) is not associated with any account." };
301 if (@person_ids > 1) {
302 $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." };
306 foreach my $pid (@person_ids) {
307 print STDERR "Now processing person with id $pid\n";
308 my $email_reset_token = $self->tempname();
309 $reset_link = $c->req->hostname()."/user/reset_password_form?token=$email_reset_token";
310 my $person = CXGN::People::Login->new( $c->dbc->dbh(), $pid);
311 $person->update_confirm_code($email_reset_token);
312 print STDERR "Sending reset link $reset_link\n";
313 $self->send_reset_email_message($c, $pid, $email, $reset_link);
316 $c->stash->{rest} = { message => "Reset link sent. Please check your email and click on the link." };
319 sub process_reset_password_form :Path('/ajax/user/process_reset_password') Args(0) {
323 my $token = $c->req->param("token");
324 my $new_password = $c->req->param("");
327 my $sp_person_id = CXGN::People::Login->get_login_by_token($c->dbc->dbh, $token);
329 my $login = CXGN::People::Login->new($c->dbc->dbh(), $sp_person_id);
330 $login->update_password($new_password);
331 $login->update_confirm_code("");
334 $c->stash->{rest} = { error => $@ };
337 $c->stash->{rest} = { message => "The password was successfully updated." };
343 sub send_reset_email_message {
347 my $private_email = shift;
348 my $reset_link = shift;
350 my $subject = "[SGN] E-mail Address Confirmation Request";
353 my $body = <<END_HEREDOC
;
355 Please
do *NOT
* reply to this message
.
356 Use
<a href
="/contact/form">the contact form
</a
> to contact us instead
.
358 Your password can be
reset using the following
link:
360 Please click
(or cut
and paste into your browser
) the following
link to
361 confirm your account
and e
-mail address
:
369 CXGN
::Contact
::send_email
($subject, $body, $private_email);
374 my $rand_string = "";
375 my $dev_urandom = new IO
::File
"</dev/urandom" || print STDERR
"Can't open /dev/urandom";
376 $dev_urandom->read( $rand_string, 16 );
377 my @bytes = unpack( "C16", $rand_string );
382 $rand_string .= chr( 65 + $_ );
385 $rand_string .= chr( 97 + ( $_ - 26 ) );
388 $rand_string .= chr( 48 + ( $_ - 52 ) );
394 sub get_login_button_html
:Path
('/ajax/user/login_button_html') Args
(0) {
398 my $production_site = $c->config->{main_production_site_url
};
399 print STDERR
"Get login button... site: $production_site\n";
401 print STDERR
"Detected logged in users...\n";
404 print STDERR
"No logged in user found!\n";
407 # if the site is a mirror, gray out the login/logout links
408 if( $c->config->{'is_mirror'} ) {
409 print STDERR
"generating login button for mirror site...\n";
411 <a style="line-height: 1.2; text-decoration: underline; background: none" href="$production_site" title="log in on main site">main site</a>
412 } elsif ( $c->config->{disable_login} ) {
413 <li class="dropdown">
414 <div class="btn-group" role="group" aria-label="..." style="height:34px; margin: 1px 0px 0px 0px" >
415 <button class="btn btn-primary disabled" type="button" style="margin: 7px 7px 0px 0px">Login</button>
421 } elsif ( $c->req->uri->path_query =~ "logout=yes") {
422 print STDERR
"generating login button for logout...\n";
424 <li class="dropdown">
425 <div class="btn-group" role="group" aria-label="..." style="height:34px; margin: 1px 0px 0px 0px" >
426 <a href="/user/login">
427 <button class="btn btn-primary" type="button" style="margin: 7px 7px 0px 0px">Login</button>
433 } elsif ( $c->user_exists ) {
434 print STDERR
"Generate login button for logged in user...\n";
435 my $sp_person_id = $c->user->get_object->get_sp_person_id;
436 my $username = $c->user->get_username();
439 <div class="btn-group" role="group" aria-label="..." style="height:34px; margin: 1px 3px 0px 0px">
440 <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>
441 <button id="navbar_lists" name="lists_link" class="btn btn-info" style="margin:7px 0px 0px 0px" type="button" title="Lists">
442 Lists <span class="glyphicon glyphicon-list-alt" ></span>
444 <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>
446 <button id="navbar_logout" class="btn btn-default glyphicon glyphicon-log-out" style="margin:6px 0px 0px 0px" type="button" onclick="location.href='/solpeople/login.pl?logout=yes';" title="Logout"></button>
452 print STDERR
"generating regular login button..\n";
454 <li class="dropdown">
455 <div class="btn-group" role="group" aria-label="..." style="height:34px; margin: 1px 0px 0px 0px" >
456 <a href="/user/login">
457 <button class="btn btn-primary" type="button" style="margin: 7px 7px 0px 0px;">Login</button>
465 print STDERR
"ERROR: $@\n";
466 $c->stash->{rest
} = { error
=> $@
};
468 return $c->stash->{rest
} = { html
=> $html };