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