root/branches/release-33/lib/MT/Auth/OpenID.pm @ 1720

Revision 1720, 11.0 kB (checked in by bchoate, 20 months ago)

Fixed usage of COMMENTER_COOKIE constant. BugId:69872

  • Property svn:keywords set to Author Date Id Revision
Line 
1# Movable Type (r) Open Source (C) 2001-2008 Six Apart, Ltd.
2# This program is distributed under the terms of the
3# GNU General Public License, version 2.
4#
5# $Id$
6
7package MT::Auth::OpenID;
8use strict;
9
10use MT::Util qw( decode_url is_valid_email escape_unicode );
11use MT::I18N qw( encode_text );
12
13sub login {
14    my $class = shift;
15    my ($app) = @_;
16    my $q = $app->{query};
17    return $app->errtrans("Invalid request.")
18        unless $q->param('blog_id');
19    my $blog = MT::Blog->load(scalar $q->param('blog_id'));
20    my %param = $app->param_hash;
21    my $csr = _get_csr(\%param, $blog) or return;
22    my $identity = $q->param('openid_url');
23    if (!$identity &&
24        (my $u = $q->param('openid_userid')) && $class->can('url_for_userid')) {
25        $identity = $class->url_for_userid($u);
26    }
27    my $claimed_identity = $csr->claimed_identity($identity);
28    if (!$claimed_identity) {
29        my ($err_code, $err_msg) = ($csr->errcode, $csr->errtext);
30        if ($err_code eq 'no_head_tag' || $err_code eq 'no_identity_server' || $err_code eq 'url_gone') {
31            $err_msg = $app->translate('The address entered does not appear to be an OpenID');
32        }
33        elsif ($err_code eq 'empty_url' || $err_code eq 'bogus_url') {
34            $err_msg = $app->translate('The text entered does not appear to be a web address');
35        }
36        elsif ($err_code eq 'url_fetch_error') {
37            $err_msg =~ s{ \A Error \s fetching \s URL: \s }{}xms;
38            $err_msg = $app->translate('Unable to connect to [_1]: [_2]', $identity, $err_msg);
39        }
40        return $app->error($app->translate("Could not verify the OpenID provided: [_1]", $err_msg));
41    }
42
43    my $root = $class->_get_root($blog);
44    my $return_to = $app->base . $app->uri . '?__mode=handle_sign_in'
45        . '&blog_id=' . $q->param('blog_id')
46        . '&static=' . $q->param('static')
47        . '&key=' . $q->param('key');
48    $return_to .= '&entry_id=' . $q->param('entry_id') if $q->param('entry_id');
49
50    my $check_url = $claimed_identity->check_url(
51        return_to => $return_to,
52        trust_root => $root,
53    );
54
55    return $app->redirect($check_url);
56}
57
58sub handle_sign_in {
59    my $class = shift;
60    my ($app, $auth_type) = @_;
61    my $q = $app->{query};
62
63    $auth_type ||= 'OpenID';
64
65    my $blog = MT::Blog->load($q->param('blog_id'));
66
67    my $cmntr;
68    my $session;
69
70    my %param = $app->param_hash;
71    my $csr = _get_csr(\%param, $blog) or return 0;
72
73    if(my $setup_url = $csr->user_setup_url( post_grant => 'return' )) {
74        return $app->redirect($setup_url);
75    } elsif(my $vident = $csr->verified_identity) {
76        my $name = $vident->url;
77        my $nick = $class->get_nickname($vident);
78
79        # Signature was valid, so create a session, etc.
80        my $enc = $app->{cfg}->PublishCharset || '';
81        my $nick_escaped = escape_unicode($nick);
82        $nick = encode_text($nick, 'utf-8', undef);
83        $session = $app->_make_commenter_session($app->make_magic_token, q(),
84                                                 $name, $nick_escaped, undef, $name);
85        unless ($session) {
86            $app->error($app->errstr() || $app->translate("Couldn't save the session"));
87            return 0;
88        }
89        $cmntr = $app->_make_commenter(
90            email       => q(),
91            nickname    => $nick,
92            name        => $name,
93            url         => $vident->url,
94            auth_type   => $auth_type,
95            external_id => _url_hash($vident->url),
96        );
97
98        if ( my $userpic = $class->get_userpicasset($vident) ) {
99            $userpic->tags('@userpic');
100            $userpic->created_by($cmntr->id);
101            $userpic->save;
102            if (my $userpic = $cmntr->userpic) {
103                # Remove the old userpic thumb so the new userpic's will be generated
104                # in its place.
105                my $thumb_file = $cmntr->userpic_file();
106                my $fmgr = MT::FileMgr->new('Local');
107                if ($fmgr->exists($thumb_file)) {
108                    $fmgr->delete($thumb_file);
109                }
110
111                $userpic->remove;
112            }
113            $cmntr->userpic_asset_id($userpic->id);
114            $cmntr->save;
115        }
116    } else {
117        # If there's no signature, then we trust the cookie.
118        my %cookies = $app->cookies();
119        my $cookie_name = MT::App::COMMENTER_COOKIE_NAME();
120        if ($cookies{$cookie_name}
121            && ($session = $cookies{$cookie_name}->value())) 
122        {
123            require MT::Session;
124            require MT::Author;
125            my $sess = MT::Session->load({id => $session});
126            $cmntr = MT::Author->load({name => $sess->name,
127                                       type => MT::Author::COMMENTER(),
128                                       auth_type => $auth_type});
129        }
130    }
131    unless ($cmntr) {
132        return 0;
133    }
134    return $cmntr;
135}
136
137sub _get_ua {
138    return MT->new_ua( { paranoid => 1 } );
139}
140
141sub _get_csr {
142    my ($params, $blog) = @_;
143    my $secret = MT->config->SecretToken;
144    my $ua = _get_ua() or return;
145    require Net::OpenID::Consumer;
146    Net::OpenID::Consumer->new(
147        ua => $ua,
148        args => $params,
149        consumer_secret => $secret,
150    );
151}
152
153sub _get_declared_foaf {
154    my ($vident) = @_;
155    my $req      = MT::Request->instance();
156    my $foaf     = $req->stash( 'foaf:' . _url_hash($vident->url) );
157    return $foaf if $foaf;
158
159    my $ua = _get_ua() or return '';
160
161    if ( my $foaf_url = $vident->declared_foaf ) {
162        my $resp = $ua->get($foaf_url);
163        if ( $resp->is_success ) {
164            $foaf = $resp->content;
165            $req->stash( 'foaf:' . _url_hash($vident->url), $foaf );
166            return $foaf;
167        }
168    }
169
170    q();
171}
172
173sub get_nickname {
174    my $class = shift;
175    my ($vident) = @_;
176    _get_nickname(@_);
177}
178
179sub _get_nickname {
180    my ($vident) = @_;
181
182    ## FOAF
183    if ( my $foaf = _get_declared_foaf($vident) ) {
184        my $name;
185
186        require XML::XPath;
187        my $xml = XML::XPath->new( xml => $foaf );
188        $xml->set_namespace('RDF', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#');
189        $xml->set_namespace('FOAF', 'http://xmlns.com/foaf/0.1/');
190        my ($name_el) = $xml->findnodes('/RDF:RDF/FOAF:Person/FOAF:name');
191        ($name_el) = $xml->findnodes('/RDF:RDF/FOAF:Person/FOAF:nick')
192            unless $name_el;
193        if ($name_el)
194        {
195            $name = $name_el->string_value;
196        }
197        $xml->cleanup;
198
199        return MT::I18N::utf8_off($name) if $name;
200    }
201
202    ## Atom
203    if(my $atom_url = $vident->declared_atom) {
204        if (my $ua = _get_ua()) {
205            my $resp = $ua->get($atom_url);
206            if($resp->is_success) {
207                my $name;
208
209                require XML::XPath;
210                my $xml = XML::XPath->new( xml => $resp->content );
211                if(my ($name_el) = $xml->findnodes('/feed/author/name')) {
212                    $name = $name_el->string_value;
213                }
214                $xml->cleanup;
215           
216                return MT::I18N::utf8_off($name) if $name;
217            }
218        }
219    }
220
221    return $vident->display ? $vident->display : $vident->url;
222}
223
224sub get_userpicasset {
225    my $class = shift;
226    my ($vident) = @_;
227    my $foaf = _get_declared_foaf($vident);
228    return undef unless $foaf;
229
230    require XML::XPath;
231    my $xml = XML::XPath->new( xml => $foaf );
232    $xml->set_namespace('RDF', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#');
233    $xml->set_namespace('FOAF', 'http://xmlns.com/foaf/0.1/');
234    my $resource = $xml->getNodeText('/RDF:RDF/FOAF:Person/FOAF:img/@RDF:resource');
235    my $url;
236    if ($resource) {
237        $url = $resource->value();
238    }
239    $xml->cleanup;
240    return undef unless $url;
241
242    return _asset_from_url($url);
243}
244
245sub _asset_from_url {
246    my ($image_url) = @_;
247    my $ua   = _get_ua() or return;
248    my $resp = $ua->get($image_url);
249    return undef unless $resp->is_success;
250    my $image = $resp->content;
251    return undef unless $image;
252    my $mimetype = $resp->header('Content-Type');
253    my $def_ext = {
254        'image/jpeg' => '.jpg',
255        'image/png'  => '.png',
256        'image/gif'  => '.gif'}->{$mimetype};
257
258    require Image::Size;
259    my ( $w, $h, $id ) = Image::Size::imgsize(\$image);
260
261    require MT::FileMgr;
262    my $fmgr = MT::FileMgr->new('Local');
263
264    my $save_path  = '%s/support/uploads/';
265    my $local_path =
266      File::Spec->catdir( MT->instance->static_file_path, 'support', 'uploads' );
267    $local_path =~ s|/$||
268      unless $local_path eq '/';    ## OS X doesn't like / at the end in mkdir().
269    unless ( $fmgr->exists($local_path) ) {
270        $fmgr->mkpath($local_path);
271    }
272    my $filename = substr($image_url, rindex($image_url, '/'));
273    if ( $filename =~ m!\.\.|\0|\|! ) {
274        return undef;
275    }
276    my ($base, $uploaded_path, $ext) = File::Basename::fileparse($filename, '\.[^\.]*');
277    $ext = $def_ext if $def_ext;  # trust content type higher than extension
278
279    # Find unique name for the file.
280    my $i = 1;
281    my $base_copy = $base;
282    while ($fmgr->exists(File::Spec->catfile($local_path, $base . $ext))) {
283        $base = $base_copy . '_' . $i++;
284    }
285
286    my $local_relative = File::Spec->catfile($save_path, $base . $ext);
287    my $local = File::Spec->catfile($local_path, $base . $ext);
288    $fmgr->put_data( $image, $local, 'upload' );
289
290    require MT::Asset;
291    my $asset_pkg = MT::Asset->handler_for_file($local);
292    return undef if $asset_pkg ne 'MT::Asset::Image';
293
294    my $asset;
295    $asset = $asset_pkg->new();
296    $asset->file_path($local_relative);
297    $asset->file_name($base.$ext);
298    my $ext_copy = $ext;
299    $ext_copy =~ s/\.//;
300    $asset->file_ext($ext_copy);
301    $asset->blog_id(0);
302
303    my $original = $asset->clone;
304    my $url = $local_relative;
305    $url  =~ s!\\!/!g;
306    $asset->url($url);
307    $asset->image_width($w);
308    $asset->image_height($h);
309    $asset->mime_type($mimetype);
310
311    $asset->save
312        or return undef;
313
314    MT->run_callbacks(
315        'api_upload_file.' . $asset->class,
316        File => $local, file => $local,
317        Url => $url, url => $url,
318        Size => length($image), size => length($image),
319        Asset => $asset, asset => $asset,
320        Type => $asset->class, type => $asset->class,
321    );
322    MT->run_callbacks(
323        'api_upload_image',
324        File => $local, file => $local,
325        Url => $url, url => $url,
326        Size => length($image), size => length($image),
327        Asset => $asset, asset => $asset,
328        Height => $h, height => $h,
329        Width => $w, width => $w,
330        Type => 'image', type => 'image',
331        ImageType => $id, image_type => $id,
332    );
333
334    $asset;
335}
336
337sub _url_hash {
338    my ($url) = @_;
339
340    if (eval { require Digest::MD5; 1; }) {
341        return Digest::MD5::md5_hex($url);
342    }
343    return substr $url, 0, 255;
344}
345
346sub _get_root {
347    my $class = shift;
348    my ($blog) = @_;
349    my $path = MT->config->CGIPath;
350    if ($path =~ m!^/!) {
351        # relative path, prepend blog domain
352        my ($blog_domain) = $blog->archive_url =~ m|(.+://[^/]+)|;
353        $path = $blog_domain . $path;
354    }
355    $path .= '/' unless $path =~ m!/$!;
356    $path;
357}
358
3591;
Note: See TracBrowser for help on using the browser.