1package CGI::Application::Plugin::Authentication::Display::Classic; 2$CGI::Application::Plugin::Authentication::Display::Classic::VERSION = '0.23'; 3use base qw(CGI::Application::Plugin::Authentication::Display); 4 5use 5.006; 6use strict; 7use warnings; 8use Carp; 9 10sub new { 11 my $class = shift; 12 my $cgiapp = shift; 13 my $self = CGI::Application::Plugin::Authentication::Display->new($cgiapp); 14 bless $self, $class; 15 return $self; 16} 17 18sub login_box { 19 my $self = shift; 20 my $credentials = $self->_cgiapp->authen->credentials; 21 my $runmode = $self->_cgiapp->get_current_runmode; 22 my $destination = $self->_cgiapp->authen->_detaint_destination || $self->_cgiapp->authen->_detaint_selfurl; 23 my $action = $self->_cgiapp->authen->_detaint_url; 24 my $username = $credentials->[0]; 25 my $password = $credentials->[1]; 26 my $login_form = $self->_cgiapp->authen->_config->{LOGIN_FORM} || {}; 27 my %options = ( 28 TITLE => 'Sign In', 29 USERNAME_LABEL => 'User Name', 30 PASSWORD_LABEL => 'Password', 31 SUBMIT_LABEL => 'Sign In', 32 COMMENT => 'Please enter your username and password in the fields below.', 33 REMEMBERUSER_OPTION => 1, 34 REMEMBERUSER_LABEL => 'Remember User Name', 35 REMEMBERUSER_COOKIENAME => 'CAPAUTHTOKEN', 36 REGISTER_URL => '', 37 REGISTER_LABEL => 'Register Now!', 38 FORGOTPASSWORD_URL => '', 39 FORGOTPASSWORD_LABEL => 'Forgot Password?', 40 INVALIDPASSWORD_MESSAGE => 'Invalid username or password<br />(login attempt %d)', 41 INCLUDE_STYLESHEET => 1, 42 FORM_SUBMIT_METHOD => 'post', 43 %$login_form, 44 ); 45 46 my $messages = ''; 47 if ( my $attempts = $self->_cgiapp->authen->login_attempts ) { 48 $messages .= '<li class="warning">' . sprintf($options{INVALIDPASSWORD_MESSAGE}, $attempts) . '</li>'; 49 } elsif ($options{COMMENT}) { 50 $messages .= "<li>$options{COMMENT}</li>"; 51 } 52 53 my $tabindex = 3; 54 my ($rememberuser, $username_value, $register, $forgotpassword, $javascript, $style) = ('','','','','',''); 55 if ($options{FOCUS_FORM_ONLOAD}) { 56 $javascript .= "document.loginform.${username}.focus();\n"; 57 } 58 if ($options{REMEMBERUSER_OPTION}) { 59 $rememberuser = qq[<input id="authen_rememberuserfield" tabindex="$tabindex" type="checkbox" name="authen_rememberuser" value="1" />$options{REMEMBERUSER_LABEL}<br />]; 60 $tabindex++; 61 $username_value = $self->_cgiapp->authen->_detaint_username($username, $options{REMEMBERUSER_COOKIENAME}); 62 $javascript .= "document.loginform.${username}.select();\n" if $username_value; 63 } 64 my $submit_tabindex = $tabindex++; 65 if ($options{REGISTER_URL}) { 66 $register = qq[<a href="$options{REGISTER_URL}" id="authen_registerlink" tabindex="$tabindex">$options{REGISTER_LABEL}</a>]; 67 $tabindex++; 68 } 69 if ($options{FORGOTPASSWORD_URL}) { 70 $forgotpassword = qq[<a href="$options{FORGOTPASSWORD_URL}" id="authen_forgotpasswordlink" tabindex="$tabindex">$options{FORGOTPASSWORD_LABEL}</a>]; 71 $tabindex++; 72 } 73 if ($options{INCLUDE_STYLESHEET}) { 74 my $login_styles = $self->_login_styles; 75 $style = <<EOS; 76<style type="text/css"> 77<!--/* <![CDATA[ */ 78$login_styles 79/* ]]> */--> 80</style> 81EOS 82 } 83 if ($javascript) { 84 $javascript = qq[<script type="text/javascript" language="JavaScript">$javascript</script>]; 85 } 86 87 my $html .= <<END; 88$style 89<form name="loginform" method="$options{FORM_SUBMIT_METHOD}" action="${action}"> 90 <div class="login"> 91 <div class="login_header"> 92 $options{TITLE} 93 </div> 94 <div class="login_content"> 95 <ul class="message"> 96${messages} 97 </ul> 98 <fieldset> 99 <label for="${username}">$options{USERNAME_LABEL}</label> 100 <input id="authen_loginfield" tabindex="1" type="text" name="${username}" size="20" value="$username_value" /><br /> 101 <label for="${password}">$options{PASSWORD_LABEL}</label> 102 <input id="authen_passwordfield" tabindex="2" type="password" name="${password}" size="20" /><br /> 103 ${rememberuser} 104 </fieldset> 105 </div> 106 <div class="login_footer"> 107 <div class="buttons"> 108 <input id="authen_loginbutton" tabindex="${submit_tabindex}" type="submit" name="authen_loginbutton" value="$options{SUBMIT_LABEL}" class="button" /> 109 ${register} 110 ${forgotpassword} 111 </div> 112 </div> 113 </div> 114 <input type="hidden" name="destination" value="${destination}" /> 115 <input type="hidden" name="rm" value="${runmode}" /> 116</form> 117$javascript 118END 119 120 return $html; 121} 122 123sub _login_styles { 124 my $self = shift; 125 my $login_form = $self->_cgiapp->authen->_config->{LOGIN_FORM} || {}; 126 my %colour = (); 127 128 $colour{base} = $login_form->{BASE_COLOUR} || '#445588'; 129 $colour{lighter} = $login_form->{LIGHTER_COLOUR} if $login_form->{LIGHTER_COLOUR}; 130 $colour{light} = $login_form->{LIGHT_COLOUR} if $login_form->{LIGHT_COLOUR}; 131 $colour{dark} = $login_form->{DARK_COLOUR} if $login_form->{DARK_COLOUR}; 132 $colour{darker} = $login_form->{DARKER_COLOUR} if $login_form->{DARKER_COLOUR}; 133 $colour{grey} = $login_form->{GREY_COLOUR} if $login_form->{GREY_COLOUR}; 134 135 my @undefined_colours = grep { ! defined $colour{$_} || index($colour{$_}, '%') >= 0 } qw(lighter light dark darker); 136 if (@undefined_colours) { 137 eval { require Color::Calc }; 138 if ($@ && $login_form->{BASE_COLOUR}) { 139 warn "Color::Calc is required when specifying a custom BASE_COLOUR, and leaving LIGHTER_COLOUR, LIGHT_COLOUR, DARK_COLOUR or DARKER_COLOUR blank or when providing percentage based colour"; 140 } 141 if ($@) { 142 $colour{base} = '#445588'; 143 $colour{lighter} = '#d0d5e1'; 144 $colour{light} = '#a2aac4'; 145 $colour{dark} = '#303c5f'; 146 $colour{darker} = '#1b2236'; 147 $colour{grey} = '#565656'; 148 } else { 149 $colour{lighter} = !$colour{lighter} 150 ? Color::Calc::light_html($colour{base}, 0.75) 151 : $colour{lighter} =~ m#(\d{2})%# 152 ? Color::Calc::light_html($colour{base}, $1 / 100) 153 : $colour{lighter}; 154 $colour{light} = !$colour{light} 155 ? Color::Calc::light_html($colour{base}, 0.5) 156 : $colour{light} =~ m#(\d{2})%# 157 ? Color::Calc::light_html($colour{base}, $1 / 100) 158 : $colour{light}; 159 $colour{dark} = !$colour{dark} 160 ? Color::Calc::dark_html($colour{base}, 0.3) 161 : $colour{dark} =~ m#(\d{2})%# 162 ? Color::Calc::dark_html($colour{base}, $1 / 100) 163 : $colour{dark}; 164 $colour{darker} = !$colour{darker} 165 ? Color::Calc::dark_html($colour{base}, 0.6) 166 : $colour{darker} =~ m#(\d{2})%# 167 ? Color::Calc::dark_html($colour{base}, $1 / 100) 168 : $colour{darker}; 169 #$colour{grey} ||= Color::Calc::bw_html($colour{base}); 170 if (!$colour{grey}) { 171 $colour{grey} = Color::Calc::bw_html($colour{base}); 172 } 173 } 174 } 175 $colour{grey} ||= '#565656'; 176 return <<END; 177div.login { 178 width: 25em; 179 margin: auto; 180 padding: 3px; 181 font-weight: bold; 182 border: 2px solid $colour{base}; 183 color: $colour{dark}; 184 font-family: sans-serif; 185} 186div.login div { 187 margin: 0; 188 padding: 0; 189 border: none; 190} 191div.login .login_header { 192 background: $colour{base}; 193 border-bottom: 1px solid $colour{darker}; 194 height: 1.5em; 195 padding: 0.45em; 196 text-align: left; 197 color: #fff; 198 font-size: 100%; 199 font-weight: bold; 200} 201div.login .login_content { 202 background: $colour{lighter}; 203 padding: 0.8em; 204 border-top: 1px solid white; 205 border-bottom: 1px solid $colour{grey}; 206 font-size: 80%; 207} 208div.login .login_footer { 209 background: $colour{light}; 210 border-top: 1px solid white; 211 border-bottom: 1px solid white; 212 text-align: left; 213 padding: 0; 214 margin: 0; 215 min-height: 2.8em; 216} 217div.login fieldset { 218 margin: 0; 219 padding: 0; 220 border: none; 221 width: 100%; 222} 223div.login label { 224 clear: left; 225 float: left; 226 padding: 0.6em 1em 0.6em 0; 227 width: 8em; 228 text-align: right; 229} 230/* image courtesy of http://www.famfamfam.com/lab/icons/silk/ */ 231#authen_loginfield { 232 background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAG5SURBVHjaYvz//z8DJQAggFiIVfh0twHn9w8KD9+/ZBT+9/cfExfvwwc87GxWAAFEtAFf3yl++/9XikHXL56BkYmJ4dKmcoUPT99PBQggRmK8ALT9v4BUBQMLrxxQMztY7N+PjwyXtk76BxBATMRoFjGewsDCx8jw9Oxyht9vboIxCDAxs/wCCCC8LoBrZv/A8PPpVoZ/39gZ7p57xcDLJ8Xw5tkdBrO8DYwAAcRElOYXaxn+/73DwC4vzyAmzsLw58kJsGaQOoAAYiJK868nDGwSXgxvjp1n+Hz7HoNawRFGmFqAAMIw4MBEDaI1gwBAAKEYsKtL/b9x2HSiNYMAQACBA3FmiqKCohrbfQ2nLobn97Yz6Br/JEozCAAEEDgh/eb6d98yYhEDBxsnw5VNZxnOffjLIKltw/D52B6GH89fMVjUnGbEFdgAAQRPiexMzAyfDk9gMJbmYbh17irDueMrGbjExBi8Oy8z4ksnAAEENuDY1S8MjjsnMSgaezJ8Z2Bm+P95PgPX6ycENYMAQACBwyDSUeQ/GzB926kLMEjwsjOwifKvcy05EkxMHgEIIEZKszNAgAEA+j3MEVmacXUAAAAASUVORK5CYII=') no-repeat 0 1px; 233 background-color: #fff; 234 border-top: solid 1px $colour{grey}; 235 border-left: solid 1px $colour{grey}; 236 border-bottom: solid 1px $colour{light}; 237 border-right: solid 1px $colour{light}; 238 padding: 2px 0 2px 18px; 239 margin: 0.3em 0; 240 width: 12em; 241} 242/* image courtesy of http://www.famfamfam.com/lab/icons/silk/ */ 243#authen_passwordfield { 244 background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKbSURBVHjaYvz//z8DPvBko+s0IJUJ5U6X8d+dhSwPEEAMIANw4ccbXKYB8f8/P+6BMYgNEkNWAxBAhDV/Pff/5+t5/39/2gcU/gc25P5qpzkwdQABxIjNCzBnS7p2Mfz5tJ+BkVWE4dWRxWA5oBcYHiyyYnj5heGAedYxR4AAwmXAf0mPWQx/3q9n+P/3I9AAMaCoBsPr4x0MDH/+MUgHrGG4P8eF4fVf9gMAAcSEK/D+/3oA1gxm/3kLJG8wSDhWMAjoeTJ8fxjNoJDQzyD0+7sDQACx4DKAkVWcgZGZG2jIV6AJfxn+/37F8OfPO6BhRxl+f/nIwC7xluHPm58MAAHEhMX5ILHp787OYvj/7zvDr7f7Gf59vw804DUwPM4x/P3+loFb0ZfhVlc1wxMu7psAAcSCEd9MjAzswoYMAppmDD9e9DKwcIkwMHFyMPx+dZnh7+9vDDxqwQx3Ji1jeMrJc9W1/JQOQAAheyFT2mctw9+vpxh+fz7A8O1JDQMrEz/QK2YMb47uZpD0SmEAmsRwu7eJ4QUX1wWXklOGIE0AAcQIim9YShOzSmf49W4xw5+PdxlYeIUYWLh9GS6vXPH+3U/Gd3K/vikzcTAzvOTkOmNXeNIUZitAALFAbF4D9N8Bhl+vJjP8/vCUgY1fkoGZ24PhysoV7178Y9vmW3M8FqZBHS3MAAIIZMDnP59P835/3Mnw98t7Bg5xNQZGNnOgzSvfv2ZgX+dbfiwVX14BCCCQAbyMrNwMDKxcDOxi/Az/WU0YLi1b8/E9K8cqr6JjGQwEAEAAMf378+/cn+//GFi5bRiYuMOBzt7w4RMH50IPIjSDAEAAsbz8+Gfdh9VFEr9//WX7//s/009uzlmuWUcqGYgEAAEGAIZWUhP4bjW1AAAAAElFTkSuQmCC') no-repeat 0 1px; 245 background-color: #fff; 246 border-top: solid 1px $colour{grey}; 247 border-left: solid 1px $colour{grey}; 248 border-bottom: solid 1px $colour{light}; 249 border-right: solid 1px $colour{light}; 250 padding: 2px 0 2px 18px; 251 margin: 0.3em 0; 252 width: 12em; 253} 254#authen_rememberuserfield { 255 clear: left; 256 margin-left: 8em; 257} 258#authen_loginfield:focus { 259 background-color: #ffc; 260 color: #000; 261} 262#authen_passwordfield:focus { 263 background-color: #ffc; 264 color: #000; 265} 266div.login a { 267 font-size: 80%; 268 color: $colour{dark}; 269} 270div.login div.buttons input { 271 border-top: solid 2px $colour{light}; 272 border-left: solid 2px $colour{light}; 273 border-bottom: solid 2px $colour{grey}; 274 border-right: solid 2px $colour{grey}; 275 background-color: $colour{lighter}; 276 padding: .2em 1em ; 277 font-size: 80%; 278 font-weight: bold; 279 color: $colour{dark}; 280} 281div.login div.buttons { 282 display: block; 283 margin: 8px 4px; 284 width: 100%; 285} 286#authen_loginbutton { 287 float: right; 288 margin-right: 1em; 289} 290#authen_registerlink { 291 display: block; 292} 293#authen_forgotpasswordlink { 294 display: block; 295} 296ul.message { 297 margin-top: 0; 298 margin-bottom: 0; 299 list-style: none; 300} 301ul.message li { 302 text-indent: -2em; 303 padding: 0px; 304 margin: 0px; 305 font-style: italic; 306} 307ul.message li.warning { 308 color: red; 309} 310END 311} 312 313=head1 NAME 314 315CGI::Application::Plugin::Authentication::Display::Classic - login box that works out of the box 316 317=head1 DESCRIPTION 318 319This module provides a login box that works out of the box but which can be 320configured to modify the styling. 321 322=head1 METHODS 323 324=head2 new 325 326The constructor must be passed the L<CGI::Application> object as the first 327non-object argument. 328 329=head2 login_box 330 331This method will return the HTML for a login box that can be 332embedded into another page. This is the same login box that is used 333in the default authen_login runmode that the plugin provides. 334 335You can set this option to customize the login form that is created when a user 336needs to be authenticated. If you wish to replace the entire login form with a 337completely custom version, then just set LOGIN_RUNMODE to point to your custom 338runmode. 339 340All of the parameters listed below are optional, and a reasonable default will 341be used if left blank: 342 343=over 4 344 345=item TITLE (default: Sign In) 346 347the heading at the top of the login box 348 349=item USERNAME_LABEL (default: User Name) 350 351the label for the user name input 352 353=item PASSWORD_LABEL (default: Password) 354 355the label for the password input 356 357=item SUBMIT_LABEL (default: Sign In) 358 359the label for the submit button 360 361=item COMMENT (default: Please enter your username and password in the fields below.) 362 363a message provided on the first login attempt 364 365=item REMEMBERUSER_OPTION (default: 1) 366 367provide a checkbox to offer to remember the users name in a cookie so that 368their user name will be pre-filled the next time they log in 369 370=item REMEMBERUSER_LABEL (default: Remember User Name) 371 372the label for the remember user name checkbox 373 374=item REMEMBERUSER_COOKIENAME (default: CAPAUTHTOKEN) 375 376the name of the cookie where the user name will be saved 377 378=item REGISTER_URL (default: <none>) 379 380the URL for the register new account link 381 382=item REGISTER_LABEL (default: Register Now!) 383 384the label for the register new account link 385 386=item FORGOTPASSWORD_URL (default: <none>) 387 388the URL for the forgot password link 389 390=item FORGOTPASSWORD_LABEL (default: Forgot Password?) 391 392the label for the forgot password link 393 394=item INVALIDPASSWORD_MESSAGE (default: Invalid username or password<br />(login attempt %d) 395 396a message given when a login failed 397 398=item INCLUDE_STYLESHEET (default: 1) 399 400use this to disable the built in style-sheet for the login box so you can provide your own custom styles 401 402=item FORM_SUBMIT_METHOD (default: post) 403 404use this to get the form to submit using 'get' instead of 'post' 405 406=item FOCUS_FORM_ONLOAD (default: 1) 407 408use this to automatically focus the login form when the page loads so a user can start typing right away. 409 410=item BASE_COLOUR (default: #445588) 411 412This is the base colour that will be used in the included login box. All other 413colours are automatically calculated based on this colour (unless you hardcode 414the colour values). In order to calculate other colours, you will need the 415Color::Calc module. If you do not have the Color::Calc module, then you will 416need to use fixed values for all of the colour options. All colour values 417besides the BASE_COLOUR can be simple percentage values (including the % sign). 418For example if you set the LIGHTER_COLOUR option to 80%, then the calculated 419colour will be 80% lighter than the BASE_COLOUR. 420 421=item LIGHT_COLOUR (default: 50% or #a2aac4) 422 423A colour that is lighter than the base colour. 424 425=item LIGHTER_COLOUR (default: 75% or #d0d5e1) 426 427A colour that is another step lighter than the light colour. 428 429=item DARK_COLOUR (default: 30% or #303c5f) 430 431A colour that is darker than the base colour. 432 433=item DARKER_COLOUR (default: 60% or #1b2236) 434 435A colour that is another step darker than the dark colour. 436 437=item GREY_COLOUR (default: #565656) 438 439A grey colour that is calculated by desaturating the base colour. 440 441 442=back 443 444 LOGIN_FORM => { 445 TITLE => 'Login', 446 SUBMIT_LABEL => 'Login', 447 REMEMBERUSER_LABEL => 1, 448 BASE_COLOUR => '#0099FF', 449 LIGHTER_COLOUR => '#AAFFFF', 450 DARK_COLOUR => '50%', 451 } 452 453=head1 BUGS 454 455This is alpha software and as such, the features and interface 456are subject to change. So please check the Changes file when upgrading. 457 458=head1 SEE ALSO 459 460L<CGI::Application>, perl(1) 461 462=head1 AUTHOR 463 464Author: Cees Hek <ceeshek@gmail.com>; Co-maintainer: Nicholas Bamber <nicholas@periapt.co.uk>. 465 466=head1 CREDITS 467 468Thanks to SiteSuite (http://www.sitesuite.com.au) for funding the 469development of this plugin and for releasing it to the world. 470 471Thanks to Christian Walde for suggesting changes to fix the incompatibility with 472L<CGI::Application::Plugin::ActionDispatch> and for help with github. 473 474=head1 LICENCE AND COPYRIGHT 475 476Copyright (c) 2005, SiteSuite. All rights reserved. 477Copyright (c) 2010, Nicholas Bamber. All rights reserved. 478 479This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. 480 481=head1 DISCLAIMER OF WARRANTY 482 483BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION. 484 485IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 486 487=cut 488 4891; 490