RequireSSL 0.06, no_cache option, work properly under restarter and POE servers
[catagits/Catalyst-Plugin-RequireSSL.git] / lib / Catalyst / Plugin / RequireSSL.pm
1 package Catalyst::Plugin::RequireSSL;
2
3 use strict;
4 use base qw/Class::Accessor::Fast/;
5 use NEXT;
6
7 our $VERSION = '0.06';
8
9 __PACKAGE__->mk_accessors( qw/_require_ssl _ssl_strip_output/ );
10
11 sub require_ssl {
12     my $c = shift;
13
14     $c->_require_ssl(1);
15
16     if ( !$c->req->secure && $c->req->method ne "POST" ) {
17         my $redir = $c->_redirect_uri('https');
18         if ( $c->config->{require_ssl}->{disabled} ) {
19             $c->log->warn( "RequireSSL: Would have redirected to $redir" );
20         }
21         else {
22             $c->_ssl_strip_output(1);
23             $c->res->redirect( $redir );
24         }
25     }
26 }
27
28 sub finalize {
29     my $c = shift;
30     
31     # Do not redirect static files (only works with Static::Simple)
32     if ( $c->isa( "Catalyst::Plugin::Static::Simple" ) ) {
33         return $c->NEXT::finalize(@_) if $c->_static_file;
34     }
35     
36     # redirect back to non-SSL mode
37     REDIRECT:
38     {
39         # No redirect if:
40         # we're not in SSL mode
41         last REDIRECT if !$c->req->secure;
42         # it's a POST request
43         last REDIRECT if $c->req->method eq "POST";
44         # we're already required to be in SSL for this request
45         last REDIRECT if $c->_require_ssl;
46         # or the user doesn't want us to redirect
47         last REDIRECT if $c->config->{require_ssl}->{remain_in_ssl};
48         
49         $c->res->redirect( $c->_redirect_uri('http') );
50     }
51
52     # do not allow any output to be displayed on the insecure page
53     if ( $c->_ssl_strip_output ) {
54         $c->res->body( '' );
55     }
56
57     return $c->NEXT::finalize(@_);
58 }
59
60 sub setup {
61     my $c = shift;
62
63     $c->NEXT::setup(@_);
64
65     # disable the plugin when running under certain engines which don't
66     # support SSL
67     if ( $c->engine =~ /Catalyst::Engine::HTTP/ ) {
68         $c->config->{require_ssl}->{disabled} = 1;
69         $c->log->warn( "RequireSSL: Disabling SSL redirection while running "
70                      . "under " . $c->engine );
71     }
72 }
73
74 sub _redirect_uri {
75     my ( $c, $type ) = @_;
76
77     # XXX: Cat needs a $c->req->host method...
78     # until then, strip off the leading protocol from base
79     if ( !$c->config->{require_ssl}->{$type} ) {
80         my $host = $c->req->base;
81         $host =~ s/^http(s?):\/\///;
82         $c->config->{require_ssl}->{$type} = $host;
83     }
84
85     if ( $c->config->{require_ssl}->{$type} !~ /\/$/xms ) {
86         $c->config->{require_ssl}->{$type} .= '/';
87     }
88
89     my $redir
90         = $type . '://' . $c->config->{require_ssl}->{$type} . $c->req->path;
91         
92     if ( scalar $c->req->param ) {
93         my @params;
94         foreach my $arg ( sort keys %{ $c->req->params } ) {
95             if ( ref $c->req->params->{$arg} ) {
96                 my $list = $c->req->params->{$arg};
97                 push @params, map { "$arg=" . $_  } sort @{$list};
98             }
99             else {
100                 push @params, "$arg=" . $c->req->params->{$arg};
101             }
102         }
103         $redir .= '?' . join( '&', @params );
104     }  
105           
106         if ( $c->config->{require_ssl}->{no_cache} ) {          
107             delete $c->config->{require_ssl}->{$type};
108         }
109         
110     return $redir;
111 }
112
113 1;
114 __END__
115
116 =head1 NAME
117
118 Catalyst::Plugin::RequireSSL - Force SSL mode on select pages
119
120 =head1 SYNOPSIS
121
122     # in MyApp.pm
123     use Catalyst;
124     MyApp->setup( qw/RequireSSL/ );
125     
126     MyApp->config->{require_ssl} = {
127         https => 'secure.mydomain.com',
128         http => 'www.mydomain.com',
129         remain_in_ssl => 0,
130                 no_cache => 0,
131     };
132
133     # in any controller methods that should be secured
134     $c->require_ssl;
135
136 =head1 DESCRIPTION
137
138 Use this plugin if you wish to selectively force SSL mode on some of your web
139 pages, for example a user login form or shopping cart.
140
141 Simply place $c->require_ssl calls in any controller method you wish to be
142 secured. 
143
144 This plugin will automatically disable itself if you are running under the
145 standalone HTTP::Daemon Catalyst server.  A warning message will be printed to
146 the log file whenever an SSL redirect would have occurred.
147
148 =head1 WARNINGS
149
150 If you utilize different servers or hostnames for non-SSL and SSL requests,
151 and you rely on a session cookie to determine redirection (i.e for a login
152 page), your cookie must be visible to both servers.  For more information, see
153 the documentation for the Session plugin you are using.
154
155 =head1 CONFIGURATION
156
157 Configuration is optional.  You may define the following configuration values:
158
159     https => $ssl_host
160     
161 If your SSL domain name is different from your non-SSL domain, set this value.
162
163     http => $non_ssl_host
164     
165 If you have set the https value above, you must also set the hostname of your
166 non-SSL server.
167
168     remain_in_ssl
169     
170 If you'd like your users to remain in SSL mode after visiting an SSL-required
171 page, you can set this option to 1.  By default, this option is disabled and
172 users will be redirected back to non-SSL mode as soon as possible.
173
174         no_cache 
175
176 If you have a wildcard certificate you will need to set this option if you are
177 using multiple domains on one instance of Catalyst.
178
179 =head1 METHODS
180
181 =head2 require_ssl
182
183 Call require_ssl in any controller method you wish to be secured.
184
185     $c->require_ssl;
186
187 The browser will be redirected to the same path on your SSL server.  POST
188 requests are never redirected.
189
190 =head1 KNOWN ISSUES
191
192 When viewing an SSL-required page that uses static files served from the
193 Static plugin, the static files are redirected to the non-SSL path.
194
195 In order to get the correct behaviour where static files are not redirected,
196 you should use the Static::Simple plugin or always serve static files
197 directly from your web server.
198
199 =head1 SEE ALSO
200
201 L<Catalyst>, L<Catalyst::Plugin::Static::Simple>
202
203 =head1 AUTHOR
204
205 Andy Grundman, <andy@hybridized.org>
206
207 =head1 CONTRIBUTORS
208
209 Simon Elliott <simon@browsing.co.uk> (support for wildcards)
210
211 =head1 COPYRIGHT
212
213 This program is free software, you can redistribute it and/or modify it under
214 the same terms as Perl itself.
215
216 =cut