root/branches/release-29/lib/MT/Auth/OpenID.pm @ 1340

Revision 1340, 10.9 kB (checked in by takayama, 22 months ago)

Fixed BugId:66594
* Generate user-pic when user-pic was not found

  • 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        if ($cookies{$app->COMMENTER_COOKIE_NAME()}
120            && ($session = $cookies{$app->COMMENTER_COOKIE_NAME()}->value())) 
121        {
122            require MT::Session;
123            require MT::Author;
124            my $sess = MT::Session->load({id => $session});
125            $cmntr = MT::Author->load({name => $sess->name,
126                                       type => MT::Author::COMMENTER(),
127                                       auth_type => $auth_type});
128        }
129    }
130    unless ($cmntr) {
131        return 0;
132    }
133    return $cmntr;
134}
135
136sub _get_ua {
137    return MT->new_ua( { paranoid => 1 } );
138}
139
140sub _get_csr {
141    my ($params, $blog) = @_;
142    my $secret = MT->config->SecretToken;
143    my $ua = _get_ua() or return;
144    require Net::OpenID::Consumer;
145    Net::OpenID::Consumer->new(
146        ua => $ua,
147        args => $params,
148        consumer_secret => $secret,
149    );
150}
151
152sub _get_declared_foaf {
153    my ($vident) = @_;
154    my $req      = MT::Request->instance();
155    my $foaf     = $req->stash( 'foaf:' . _url_hash($vident->url) );
156    return $foaf if $foaf;
157
158    my $ua = _get_ua() or return '';
159
160    if ( my $foaf_url = $vident->declared_foaf ) {
161        my $resp = $ua->get($foaf_url);
162        if ( $resp->is_success ) {
163            $foaf = $resp->content;
164            $req->stash( 'foaf:' . _url_hash($vident->url), $foaf );
165            return $foaf;
166        }
167    }
168
169    q();
170}
171
172sub get_nickname {
173    my $class = shift;
174    my ($vident) = @_;
175    _get_nickname(@_);
176}
177
178sub _get_nickname {
179    my ($vident) = @_;
180
181    ## FOAF
182    if ( my $foaf = _get_declared_foaf($vident) ) {
183        my $name;
184
185        require XML::XPath;
186        my $xml = XML::XPath->new( xml => $foaf );
187        $xml->set_namespace('RDF', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#');
188        $xml->set_namespace('FOAF', 'http://xmlns.com/foaf/0.1/');
189        my ($name_el) = $xml->findnodes('/RDF:RDF/FOAF:Person/FOAF:name');
190        ($name_el) = $xml->findnodes('/RDF:RDF/FOAF:Person/FOAF:nick')
191            unless $name_el;
192        if ($name_el)
193        {
194            $name = $name_el->string_value;
195        }
196        $xml->cleanup;
197
198        return MT::I18N::utf8_off($name) if $name;
199    }
200
201    ## Atom
202    if(my $atom_url = $vident->declared_atom) {
203        if (my $ua = _get_ua()) {
204            my $resp = $ua->get($atom_url);
205            if($resp->is_success) {
206                my $name;
207
208                require XML::XPath;
209                my $xml = XML::XPath->new( xml => $resp->content );
210                if(my ($name_el) = $xml->findnodes('/feed/author/name')) {
211                    $name = $name_el->string_value;
212                }
213                $xml->cleanup;
214           
215                return MT::I18N::utf8_off($name) if $name;
216            }
217        }
218    }
219
220    return $vident->display ? $vident->display : $vident->url;
221}
222
223sub get_userpicasset {
224    my $class = shift;
225    my ($vident) = @_;
226    my $foaf = _get_declared_foaf($vident);
227    return undef unless $foaf;
228
229    require XML::XPath;
230    my $xml = XML::XPath->new( xml => $foaf );
231    $xml->set_namespace('RDF', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#');
232    $xml->set_namespace('FOAF', 'http://xmlns.com/foaf/0.1/');
233    my $resource = $xml->getNodeText('/RDF:RDF/FOAF:Person/FOAF:img/@RDF:resource');
234    my $url;
235    if ($resource) {
236        $url = $resource->value();
237    }
238    $xml->cleanup;
239    return undef unless $url;
240
241    return _asset_from_url($url);
242}
243
244sub _asset_from_url {
245    my ($image_url) = @_;
246    my $ua   = _get_ua() or return;
247    my $resp = $ua->get($image_url);
248    return undef unless $resp->is_success;
249    my $image = $resp->content;
250    return undef unless $image;
251    my $mimetype = $resp->header('Content-Type');
252    my $def_ext = {
253        'image/jpeg' => '.jpg',
254        'image/png'  => '.png',
255        'image/gif'  => '.gif'}->{$mimetype};
256
257    require Image::Size;
258    my ( $w, $h, $id ) = Image::Size::imgsize(\$image);
259
260    require MT::FileMgr;
261    my $fmgr = MT::FileMgr->new('Local');
262
263    my $save_path  = '%s/support/uploads/';
264    my $local_path =
265      File::Spec->catdir( MT->instance->static_file_path, 'support', 'uploads' );
266    $local_path =~ s|/$||
267      unless $local_path eq '/';    ## OS X doesn't like / at the end in mkdir().
268    unless ( $fmgr->exists($local_path) ) {
269        $fmgr->mkpath($local_path);
270    }
271    my $filename = substr($image_url, rindex($image_url, '/'));
272    if ( $filename =~ m!\.\.|\0|\|! ) {
273        return undef;
274    }
275    my ($base, $uploaded_path, $ext) = File::Basename::fileparse($filename, '\.[^\.]*');
276    $ext = $def_ext if $def_ext;  # trust content type higher than extension
277
278    # Find unique name for the file.
279    my $i = 1;
280    my $base_copy = $base;
281    while ($fmgr->exists(File::Spec->catfile($local_path, $base . $ext))) {
282        $base = $base_copy . '_' . $i++;
283    }
284
285    my $local_relative = File::Spec->catfile($save_path, $base . $ext);
286    my $local = File::Spec->catfile($local_path, $base . $ext);
287    $fmgr->put_data( $image, $local, 'upload' );
288
289    require MT::Asset;
290    my $asset_pkg = MT::Asset->handler_for_file($local);
291    return undef if $asset_pkg ne 'MT::Asset::Image';
292
293    my $asset;
294    $asset = $asset_pkg->new();
295    $asset->file_path($local_relative);
296    $asset->file_name($base.$ext);
297    my $ext_copy = $ext;
298    $ext_copy =~ s/\.//;
299    $asset->file_ext($ext_copy);
300    $asset->blog_id(0);
301
302    my $original = $asset->clone;
303    my $url = $local_relative;
304    $url  =~ s!\\!/!g;
305    $asset->url($url);
306    $asset->image_width($w);
307    $asset->image_height($h);
308    $asset->mime_type($mimetype);
309
310    $asset->save
311        or return undef;
312
313    MT->run_callbacks(
314        'api_upload_file.' . $asset->class,
315        File => $local, file => $local,
316        Url => $url, url => $url,
317        Size => length($image), size => length($image),
318        Asset => $asset, asset => $asset,
319        Type => $asset->class, type => $asset->class,
320    );
321    MT->run_callbacks(
322        'api_upload_image',
323        File => $local, file => $local,
324        Url => $url, url => $url,
325        Size => length($image), size => length($image),
326        Asset => $asset, asset => $asset,
327        Height => $h, height => $h,
328        Width => $w, width => $w,
329        Type => 'image', type => 'image',
330        ImageType => $id, image_type => $id,
331    );
332
333    $asset;
334}
335
336sub _url_hash {
337    my ($url) = @_;
338
339    if (eval { require Digest::MD5; 1; }) {
340        return Digest::MD5::md5_hex($url);
341    }
342    return substr $url, 0, 255;
343}
344
345sub _get_root {
346    my $class = shift;
347    my ($blog) = @_;
348    my $path = MT->config->CGIPath;
349    if ($path =~ m!^/!) {
350        # relative path, prepend blog domain
351        my ($blog_domain) = $blog->archive_url =~ m|(.+://[^/]+)|;
352        $path = $blog_domain . $path;
353    }
354    $path .= '/' unless $path =~ m!/$!;
355    $path;
356}
357
3581;
Note: See TracBrowser for help on using the browser.