root/branches/release-36/lib/MT/Auth/TypeKey.pm @ 2062

Revision 2062, 8.1 kB (checked in by bchoate, 19 months ago)

Updates to blog-side javascript regarding user state and permissions. BugId:79077,69644,67754,69814,79258,62643. Fixed declarations for conditional tags. BugId:79476. Display auth'd user nickname rather than name from comment object. BugId:79475

  • 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::TypeKey;
8use strict;
9
10use MT::Util qw( decode_url is_valid_email escape_unicode );
11use MT::I18N qw( encode_text );
12
13sub handle_sign_in {
14    my $class = shift;
15    my ($app, $auth_type) = @_;
16    my $q = $app->{query};
17
18    my $sig_str = $q->param('sig');
19    unless ($sig_str) {
20        $app->error($app->translate('Sign in requires a secure signature.'));
21        return 0;
22    }
23
24    my $entry_id = $q->param('entry_id');
25    my $entry = MT::Entry->load($entry_id)
26        or return 0;
27    my $blog = MT::Blog->load($q->param('blog_id') || $entry->blog_id)
28        or return 0;
29
30    my $ts = $q->param('ts') || "";
31    my $email = $q->param('email') || "";
32    my $name = $q->param('name') || "";
33    my $nick = $q->param('nick') || "";
34    my $cmntr;
35    my $session;
36    if ($sig_str) {
37        if (!$class->_validate_signature($app, $sig_str, 
38                                       token => $blog->effective_remote_auth_token,
39                                       email => decode_url($email),
40                                       name => decode_url($name),
41                                       nick => decode_url($nick),
42                                       ts => $ts))
43        {
44            # Signature didn't match, or timestamp was out of date.
45            # This implies tampering, not a user mistake.
46            $app->error($app->translate("The sign-in validation failed."));
47            return 0;
48        }
49
50        if ($blog->require_typekey_emails && !is_valid_email($email)) {
51            $q->param('email', '');  # blank out email address since it's invalid
52            $app->error($app->translate("This weblog requires commenters to pass an email address. If you'd like to do so you may log in again, and give the authentication service permission to pass your email address."));
53            return 0;
54        }
55
56        my $url = $app->{cfg}->IdentityURL;
57        $url .= "/" unless $url =~ m|/$|;
58        $url .= $name;
59
60        # Signature was valid, so create a session, etc.
61        my $enc = $app->{cfg}->PublishCharset || '';
62        my $nick_escaped = escape_unicode($nick);
63        $nick = encode_text($nick, 'utf-8', undef);
64        $session = $app->make_commenter_session($sig_str, $email,
65                                                 $name, $nick_escaped, undef, $url);
66        unless ($session) {
67            $app->error($app->errstr() || $app->translate("Couldn't save the session"));
68            return 0;
69        }
70        $cmntr = $app->_make_commenter(
71            email => $email,
72            nickname => $nick,
73            name => $name,
74            url => $url,
75            auth_type => $auth_type,
76        );
77    } else {
78        # If there's no signature, then we trust the cookie.
79        my %cookies = $app->cookies();
80        my $cookie_name = MT::App::COMMENTER_COOKIE_NAME();
81        if ($cookies{$cookie_name}
82            && ($session = $cookies{$cookie_name}->value())) 
83        {
84            require MT::Session;
85            require MT::Author;
86            my $sess = MT::Session->load({id => $session})
87                or return 0;
88            $cmntr = MT::Author->load({name => $sess->name,
89                                       type => MT::Author::COMMENTER(),
90                                       auth_type => $auth_type})
91                or return 0;
92            if ($blog->require_typekey_emails
93                && !is_valid_email($cmntr->email))
94            {
95                $app->error($app->translate("This blog requires commenters to provide an email address"));
96                return 0;
97            }
98        }
99    }
100    if ($q->param('sig') && !$cmntr) {
101        return 0;
102    }
103    return $cmntr;
104}
105
106my $SIG_WINDOW = 60 * 10;  # ten minute handoff between TP and MT
107
108sub _validate_signature {
109    my $class = shift;
110    my ($app, $sig_str, %params) = @_;
111
112    # the DSA sig parameter is composed of the two pieces of the
113    # real DSA sig, packed in Base64, separated by a colon.
114
115#    my ($r, $s) = split /:/, decode_url($sig_str);
116    my ($r, $s) = split /:/, $sig_str;
117    $r =~ s/ /+/g;
118    $s =~ s/ /+/g;
119
120    $params{email} =~ s/ /+/g;
121    require MIME::Base64;
122    import MIME::Base64 qw(decode_base64);
123    use MT::Util qw(bin2dec);
124    $r = bin2dec(decode_base64($r));
125    $s = bin2dec(decode_base64($s));
126
127    my $sig = {'s' => $s,
128               'r' => $r};
129    my $timer = time;
130    require MT::Util; import MT::Util ('dsa_verify');
131    my $msg;
132    if ($app->{cfg}->TypeKeyVersion eq '1.1') {
133        $msg = ($params{email} . "::" . $params{name} . "::" .
134                $params{nick} . "::" . $params{ts} . "::" . $params{token});
135    } else {
136        $msg = ($params{email} . "::" . $params{name} . "::" .
137                $params{nick} . "::" . $params{ts});
138    }
139
140    my $dsa_key;
141    require MT::Session;
142    $dsa_key = eval { MT::Session->load({id => 'KY',
143                                         kind => 'KY'}); } || undef; 
144    if ($dsa_key) {
145        if ($dsa_key->start() < time - 24 * 60 * 60) {
146            $dsa_key = undef;
147        }
148        $dsa_key = $dsa_key->data if $dsa_key;
149    }
150    if ( ! $dsa_key ) {
151        # Load the override key
152        $dsa_key = $app->{cfg}->get('SignOnPublicKey');
153    }
154    # Load the DSA key from the RegKeyURL
155    my $key_location = $app->{cfg}->RegKeyURL;
156    if (!$dsa_key && $key_location) {
157        my $ua = $app->new_ua;
158        unless ($ua) {
159            my $err = $app->translate("Couldn't get public key from url provided");
160            $app->log({
161                message => $err,
162                level => MT::Log::ERROR(),
163                class => 'system',
164                category => 'commenter_authentication',
165            });
166            return $app->error($err);
167        }
168        my $req = new HTTP::Request(GET => $key_location);
169        my $resp = $ua->request($req);
170        unless ($resp->is_success()) {
171            my $err = $app->translate("Couldn't get public key from url provided");
172            $app->log({
173                message => $err,
174                level => MT::Log::ERROR(),
175                class => 'system',
176                category => 'commenter_authentication',
177            });
178            return $app->error($err);
179        }
180        # TBD: Check the content-type
181        $dsa_key = $resp->content();
182
183        require MT::Session;
184        my $key_cache = new MT::Session();
185
186        my @chs = ('a' .. 'z', '0' .. '9');
187        $key_cache->set_values({id => 'KY',
188                                data => $dsa_key,
189                                kind => 'KY',
190                                start => time});
191        $key_cache->save();
192    }
193    if (!$dsa_key) {
194        $app->log({
195            message => $app->translate("No public key could be found to validate registration."),
196            level => MT::Log::ERROR(),
197            class => 'system',
198            category => 'commenter_authentication',
199        });
200        return $app->error($app->translate(
201                    "No public key could be found to validate registration."));
202    }
203    my ($p) = $dsa_key =~ /p=([0-9a-f]*)/i;
204    my ($q) = $dsa_key =~ /q=([0-9a-f]*)/i;
205    my ($g) = $dsa_key =~ /g=([0-9a-f]*)/i;
206    my ($pub_key) = $dsa_key =~ /pub_key=([0-9a-f]*)/i;
207    $dsa_key = {p=>$p, q=>$q, g=>$g, pub_key=>$pub_key};
208    my $valid = dsa_verify(Key => $dsa_key,
209                           Signature => $sig,
210                           Message => $msg);
211    $timer = time - $timer;
212
213    $app->log({
214        message => $app->translate("TypeKey signature verif'n returned [_1] in [_2] seconds verifying [_3] with [_4]",
215            ($valid ? "VALID" : "INVALID"), $timer, $msg, $sig_str),
216        class => 'system',
217        level => MT::Log::WARNING(),
218    }) unless $valid;
219
220    $app->log({
221        message => $app->translate("The TypeKey signature is out of date ([_1] seconds old). Ensure that your server's clock is correct", ($params{ts} - time)),
222        class => 'system',
223        level => MT::Log::WARNING(),
224    }) unless ($params{ts} + $SIG_WINDOW >= time);
225
226    return ($valid && $params{ts} + $SIG_WINDOW >= time);
227}
228
2291;
Note: See TracBrowser for help on using the browser.