source: bookmarks/trunk/lib/BookmarksApp.pm @ 118

Last change on this file since 118 was 118, checked in by peter, 9 years ago

Moved the digest_key generation into the BookmarksApp, so that it only happens on app startup.

File size: 5.2 KB
Line 
1package BookmarksApp;
2
3=head1 NAME
4
5BookmarksApp
6
7=head1 SYNOPSIS
8
9    use BookmarksApp;
10    use Digest::MD5 qw{md5_hex};
11
12    my $username = '...';
13    my $password = '...';
14    my $app = BookmarksApp->new({
15        config => {
16            dbname => 'bookmarks.db',
17
18            # set these if you want non-GET requests to require authentication
19            auth            => 1,
20            digest_key      => 'secret',
21            digest_password => md5_hex("$username:Bookmarks:$password"),
22           
23            # set this if the app is running behind a proxy server
24            proxy_ip  => '...',
25        },
26    });
27
28    # returns the coderef appropriate for use in an app.psgi,
29    # or for passing the Plack::Runner, etc.
30    $app->to_app;
31
32=cut
33
34use strict;
35use warnings;
36
37use parent qw{Plack::Component};
38use Plack::Util::Accessor qw{config _app _controller};
39
40use YAML;
41use Plack::Builder;
42use Plack::Request;
43use Router::Resource;
44
45use Bookmarks::Controller;
46
47sub prepare_app {
48    my $self = shift;
49
50    my $config = $self->config;
51
52    my $router = router {
53        resource '/' => sub {
54            GET {
55                # check for a uri param, and if there is one present,
56                # see if a bookmark for that URI already exists; if so
57                # redirect to that bookmark, and if not, show the form
58                # to create a new bookmark
59                if (defined $self->_controller->request->param('uri')) {
60                    return $self->_controller->find_or_new;
61                }
62
63                # otherwise return the sidebar
64                return $self->_controller->sidebar;
65            };
66            POST {
67                # create the bookmark and redirect to the new bookmark's edit form
68                return $self->_controller->create_and_redirect;
69            };
70        };
71
72        resource '/list' => sub {
73            GET {
74                return $self->_controller->list;
75            };
76        };
77
78        resource '/feed' => sub {
79            GET {
80                return $self->_controller->feed;
81            };
82        };
83
84        resource '/tags' => sub {
85            GET {
86                my ($env, $params) = @_;
87                return $self->_controller->tag_tree;
88            };
89        };
90        resource '/tags/*' => sub {
91            GET {
92                my ($env, $params) = @_;
93                my $tag_path = (@{ $params->{splat} })[0];
94                return $self->_controller->tag_tree([ split m{/}, $tag_path ]);
95            };
96        };
97
98        resource '/{id}' => sub {
99            GET {
100                my ($env, $params) = @_;
101                return $self->_controller->view($params->{id});
102            };
103            POST {
104                my ($env, $params) = @_;
105                return $self->_controller->update_and_redirect($params->{id});
106            };
107        };
108
109        resource '/{id}/{field}' => sub {
110            GET {
111                my ($env, $params) = @_;
112                return $self->_controller->view_field($params->{id}, $params->{field});
113            };
114        };
115
116    };
117
118    # if configured for auth, read in the htdigest database file
119    # and store the password hashes keyed by username
120    my %password_hash_for;
121    if ($config->{auth}) {
122        $config->{realm} ||= 'Bookmarks';
123        $config->{htdigest} or die "No htdigest configured for authentication\n";
124
125        # if authentication is enabled and no digest_key is provided, generate one
126        # don't do this if it isn't needed, since this is sometimes not a fast operation
127        if (!$config->{digest_key}) {
128            warn "Generating digest authentication secret...\n";
129            require Bytes::Random::Secure;
130            $config->{digest_key} = Bytes::Random::Secure::random_bytes_base64(16, '');
131        }
132
133        open my $htdigest, '<', $config->{htdigest} or die "Can't open $$config{htdigest}\n";
134        while (my $credentials = <$htdigest>) {
135            chomp $credentials;
136            my ($username, $realm, $password_hash) = split /:/, $credentials;
137            # only add password digests for the configured realm
138            if ($realm eq $config->{realm}) {
139                $password_hash_for{$username} = $password_hash;
140            }
141        }
142        close $htdigest;
143    }
144
145    $self->_app(
146        builder {
147            enable_if { $_[0]->{REMOTE_ADDR} eq $config->{proxy_ip} } 'ReverseProxy'
148                if $config->{proxy_ip};
149            enable_if { $_[0]->{REQUEST_METHOD} ne 'GET' } 'Auth::Digest', (
150                realm           => $config->{realm},
151                secret          => $config->{digest_key},
152                password_hashed => 1,
153                authenticator   => sub {
154                    my ($username, $env) = @_;
155                    return $password_hash_for{$username};
156                },
157            ) if $config->{auth};
158            sub { $router->dispatch(shift); };
159        }
160    );
161    $self->_controller(
162        Bookmarks::Controller->new({
163            dbname  => $self->config->{dbname},
164        })
165    );
166}
167
168sub call {
169    my $self = shift;
170    my $env = shift;
171
172    # initialize the controller based on this request
173    $self->_controller->request(Plack::Request->new($env));
174
175    # dispatch to the app
176    $self->_app->($env);
177}
178
179# module return
1801;
181
182=head1 AUTHOR
183
184Peter Eichman <peichman@cpan.org>
185
186=cut
Note: See TracBrowser for help on using the repository browser.