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