OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "remoting/host/url_fetcher_token_validator_factory.h" | |
6 | |
7 #include <set> | |
8 | |
9 #include "base/base64.h" | |
10 #include "base/bind.h" | |
11 #include "base/callback.h" | |
12 #include "base/json/json_reader.h" | |
13 #include "base/location.h" | |
14 #include "base/logging.h" | |
15 #include "base/single_thread_task_runner.h" | |
16 #include "base/values.h" | |
17 #include "crypto/random.h" | |
18 #include "googleurl/src/gurl.h" | |
19 #include "net/base/escape.h" | |
20 #include "net/url_request/url_fetcher.h" | |
21 #include "net/url_request/url_fetcher_delegate.h" | |
22 #include "net/url_request/url_request_status.h" | |
23 #include "remoting/base/rsa_key_pair.h" | |
24 | |
25 namespace { | |
26 | |
27 // Length in bytes of the cryptographic nonce used to salt the token scope. | |
28 const size_t kNonceLength = 16; // 128 bits. | |
Sergey Ulanov
2013/03/28 22:34:54
indentation
rmsousa
2013/04/04 22:13:43
Done.
| |
29 | |
30 } | |
31 | |
32 namespace remoting { | |
33 | |
34 class UrlFetcherTokenValidator | |
35 : public net::URLFetcherDelegate, | |
36 public protocol::ThirdPartyHostAuthenticator::TokenValidator { | |
37 public: | |
38 UrlFetcherTokenValidator( | |
39 const GURL& token_url, | |
40 const GURL& token_validation_url, | |
41 scoped_refptr<RsaKeyPair> key_pair, | |
42 const std::string& local_jid, | |
43 const std::string& remote_jid, | |
44 scoped_refptr<net::URLRequestContextGetter> request_context_getter) | |
45 : token_url_(token_url), | |
46 token_validation_url_(token_validation_url), | |
47 key_pair_(key_pair), | |
48 request_context_getter_(request_context_getter) { | |
49 DCHECK(token_url_.is_valid()); | |
50 DCHECK(token_validation_url_.is_valid()); | |
51 DCHECK(key_pair_); | |
52 token_scope_ = CreateScope(local_jid, remote_jid); | |
53 } | |
54 | |
55 virtual ~UrlFetcherTokenValidator() { | |
56 } | |
57 | |
58 // TokenValidator interface. | |
59 virtual void ValidateThirdPartyToken( | |
60 const std::string& token, | |
61 const base::Callback<void( | |
62 const std::string& shared_secret)>& on_token_validated) OVERRIDE { | |
63 DCHECK(!request_); | |
64 DCHECK(!on_token_validated.is_null()); | |
65 | |
66 on_token_validated_ = on_token_validated; | |
67 | |
68 std::string post_body = | |
69 "code=" + net::EscapeUrlEncodedData(token, true) + | |
70 "&client_id=" + net::EscapeUrlEncodedData( | |
71 key_pair_->GetPublicKey(), true) + | |
72 "&client_secret=" + net::EscapeUrlEncodedData( | |
73 key_pair_->SignMessage(token), true) + | |
74 "&grant_type=authorization_code"; | |
75 request_.reset(net::URLFetcher::Create( | |
76 token_validation_url_, net::URLFetcher::POST, this)); | |
77 request_->SetUploadData("application/x-www-form-urlencoded", post_body); | |
Sergey Ulanov
2013/03/28 22:34:54
Can we use json here instead of url-encoding the d
rmsousa
2013/03/28 23:12:49
I'm trying to follow the OAuth protocol, which use
Sergey Ulanov
2013/03/28 23:39:39
I'm not sure why we need to worry about OAuth here
rmsousa
2013/04/04 22:13:43
Yes, the protocol will be OAuth. It's possible to
| |
78 request_->SetRequestContext(request_context_getter_); | |
79 request_->Start(); | |
80 } | |
81 | |
82 virtual const GURL& token_url() const OVERRIDE { | |
83 return token_url_; | |
84 } | |
85 | |
86 virtual const std::string& token_scope() const OVERRIDE { | |
87 return token_scope_; | |
88 } | |
89 | |
90 // URLFetcherDelegate interface. | |
91 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE { | |
92 DCHECK_EQ(request_.get(), source); | |
93 std::string shared_token = GetSharedSecretFromResponse(source); | |
94 on_token_validated_.Run(shared_token); | |
95 request_.reset(); | |
96 } | |
97 | |
98 private: | |
99 bool IsValidScope(const std::string& token_scope) { | |
100 // TODO(rmsousa): Deal with reordering/subsets/supersets/aliases/etc. | |
101 return token_scope == token_scope_; | |
102 } | |
103 | |
104 static std::string CreateScope(const std::string& local_jid, | |
105 const std::string& remote_jid) { | |
106 char nonce_bytes[kNonceLength]; | |
107 crypto::RandBytes(nonce_bytes, kNonceLength); | |
108 std::string nonce; | |
109 bool success = base::Base64Encode(nonce_bytes, &nonce); | |
110 DCHECK(success); | |
111 return "client:" + remote_jid + " host:" + local_jid + " nonce:" + nonce; | |
112 } | |
113 | |
114 std::string GetSharedSecretFromResponse(const net::URLFetcher* source) { | |
Sergey Ulanov
2013/03/28 22:34:54
Maybe call it ProcessResponse?
Sergey Ulanov
2013/03/28 22:34:54
|source| is a meaningless name in this context, bu
rmsousa
2013/04/04 22:13:43
Done.
rmsousa
2013/04/04 22:13:43
Done.
| |
115 std::string shared_secret; | |
116 | |
117 // Verify that we got a successful response. | |
118 int response = source->GetResponseCode(); | |
119 net::URLRequestStatus status = source->GetStatus(); | |
120 std::string data; | |
121 if (!status.is_success() || response != 200) { | |
122 LOG(ERROR) << | |
Sergey Ulanov
2013/03/28 22:34:54
nit: wrap << operator
rmsousa
2013/04/04 22:13:43
Done.
| |
123 "Error " << response << " validating token: '" << data << "'"; | |
124 return shared_secret; | |
Sergey Ulanov
2013/03/28 22:34:54
This is confusing. Maybe better to return std::str
rmsousa
2013/04/04 22:13:43
Done.
| |
125 } | |
126 | |
127 // Decode the JSON data from the response. | |
128 source->GetResponseAsString(&data); | |
129 scoped_ptr<base::Value> value(base::JSONReader::Read(data)); | |
130 if (!value.get() || value->GetType() != base::Value::TYPE_DICTIONARY) { | |
131 LOG(ERROR) << "Invalid token validation response: '" << data << "'"; | |
132 return shared_secret; | |
133 } | |
134 | |
135 // Get the scope from the response, and verify that it is valid. | |
136 DictionaryValue* dict = static_cast<DictionaryValue*>(value.get()); | |
Sergey Ulanov
2013/03/28 22:34:54
Ouch. Instead of downcasting using static_cast<> u
rmsousa
2013/04/04 22:13:43
Done.
| |
137 std::string token_scope; | |
138 dict->GetStringWithoutPathExpansion("scope", &token_scope); | |
139 if (!IsValidScope(token_scope)) { | |
140 LOG(ERROR) << "Invalid scope: '" << token_scope | |
141 << "', expected: '" << token_scope_ <<"'."; | |
142 return shared_secret; | |
143 } | |
144 | |
145 // Everything is valid, so return the shared secret to the caller. | |
146 dict->GetStringWithoutPathExpansion("access_token", &shared_secret); | |
147 return shared_secret; | |
148 } | |
149 | |
150 scoped_ptr<net::URLFetcher> request_; | |
151 GURL token_url_; | |
152 GURL token_validation_url_; | |
153 scoped_refptr<RsaKeyPair> key_pair_; | |
154 std::string token_scope_; | |
155 scoped_refptr<net::URLRequestContextGetter> request_context_getter_; | |
156 base::Callback<void(const std::string& shared_secret)> on_token_validated_; | |
157 | |
158 DISALLOW_COPY_AND_ASSIGN(UrlFetcherTokenValidator); | |
159 }; | |
160 | |
161 UrlFetcherTokenValidatorFactory::UrlFetcherTokenValidatorFactory( | |
162 scoped_refptr<net::URLRequestContextGetter> request_context_getter) | |
163 : request_context_getter_(request_context_getter) { | |
164 } | |
165 | |
166 UrlFetcherTokenValidatorFactory::~UrlFetcherTokenValidatorFactory() { | |
167 } | |
168 | |
169 scoped_ptr<protocol::ThirdPartyHostAuthenticator::TokenValidator> | |
170 UrlFetcherTokenValidatorFactory::CreateTokenValidator( | |
171 const GURL& token_url, | |
172 const GURL& token_validation_url, | |
173 scoped_refptr<RsaKeyPair> key_pair, | |
174 const std::string& local_jid, | |
175 const std::string& remote_jid) { | |
176 return scoped_ptr<protocol::ThirdPartyHostAuthenticator::TokenValidator>( | |
177 new UrlFetcherTokenValidator(token_url, token_validation_url, key_pair, | |
178 local_jid, remote_jid, request_context_getter_)); | |
Sergey Ulanov
2013/03/28 22:34:54
indentation
rmsousa
2013/04/04 22:13:43
Done.
| |
179 } | |
180 | |
181 } // namespace remoting | |
OLD | NEW |