Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(609)

Side by Side Diff: ios/chrome/browser/passwords/credential_manager.mm

Issue 1456983002: Move JS-related password manager code upstream (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Indent fixed, capitalisation fixed, typo fixed Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 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 #import "ios/chrome/browser/passwords/credential_manager.h"
6
7 #include "base/ios/ios_util.h"
8 #import "base/ios/weak_nsobject.h"
9 #include "base/mac/bind_objc_block.h"
10 #include "base/memory/scoped_vector.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/strings/sys_string_conversions.h"
13 #include "components/password_manager/core/browser/password_store_consumer.h"
14 #include "components/password_manager/core/common/credential_manager_types.h"
15 #include "components/password_manager/core/common/password_manager_pref_names.h"
16 #import "ios/chrome/browser/passwords/js_credential_manager.h"
17 #import "ios/web/public/url_scheme_util.h"
18 #include "ios/web/public/web_state/credential.h"
19 #include "ios/web/public/web_state/url_verification_constants.h"
20 #include "ios/web/public/web_state/web_state.h"
21
22 namespace {
23
24 // Converts a password_manager::CredentialInfo to a web::Credential.
25 web::Credential WebCredentialFromCredentialInfo(
26 const password_manager::CredentialInfo& credential_info) {
27 web::Credential credential;
28 switch (credential_info.type) {
29 case password_manager::CredentialType::CREDENTIAL_TYPE_EMPTY:
30 credential.type = web::CredentialType::CREDENTIAL_TYPE_EMPTY;
31 break;
32 case password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD:
33 credential.type = web::CredentialType::CREDENTIAL_TYPE_PASSWORD;
34 break;
35 case password_manager::CredentialType::CREDENTIAL_TYPE_FEDERATED:
36 credential.type = web::CredentialType::CREDENTIAL_TYPE_FEDERATED;
37 break;
38 }
39 credential.id = credential_info.id;
40 credential.name = credential_info.name;
41 credential.avatar_url = credential_info.icon;
42 credential.password = credential_info.password;
43 credential.federation_url = credential_info.federation;
44 return credential;
45 }
46
47 // Converts a web::Credential to a password_manager::CredentialInfo.
48 password_manager::CredentialInfo CredentialInfoFromWebCredential(
49 const web::Credential& credential) {
50 password_manager::CredentialInfo credential_info;
51 switch (credential.type) {
52 case web::CredentialType::CREDENTIAL_TYPE_EMPTY:
53 credential_info.type =
54 password_manager::CredentialType::CREDENTIAL_TYPE_EMPTY;
55 break;
56 case web::CredentialType::CREDENTIAL_TYPE_PASSWORD:
57 credential_info.type =
58 password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD;
59 break;
60 case web::CredentialType::CREDENTIAL_TYPE_FEDERATED:
61 credential_info.type =
62 password_manager::CredentialType::CREDENTIAL_TYPE_FEDERATED;
63 break;
64 }
65 credential_info.id = credential.id;
66 credential_info.name = credential.name;
67 credential_info.icon = credential.avatar_url;
68 credential_info.password = credential.password;
69 credential_info.federation = credential.federation_url;
70 return credential_info;
71 }
72
73 } // namespace
74
75 CredentialManager::CredentialManager(
76 web::WebState* web_state,
77 password_manager::PasswordManagerClient* client,
78 password_manager::PasswordManagerDriver* driver,
79 JSCredentialManager* js_manager)
80 : web::WebStateObserver(web_state),
81 pending_request_(nullptr),
82 form_manager_(nullptr),
83 js_manager_([js_manager retain]),
84 client_(client),
85 driver_(driver),
86 weak_factory_(this) {
87 zero_click_sign_in_enabled_.Init(
88 password_manager::prefs::kPasswordManagerAutoSignin, client_->GetPrefs());
89 }
90
91 CredentialManager::~CredentialManager() = default;
92
93 void CredentialManager::PageLoaded(
94 web::PageLoadCompletionStatus load_completion_status) {
95 // Ensure the JavaScript is loaded when the page finishes loading.
96 web::URLVerificationTrustLevel trust_level =
97 web::URLVerificationTrustLevel::kNone;
98 const GURL page_url(web_state()->GetCurrentURL(&trust_level));
99 if (!base::ios::IsRunningOnIOS8OrLater() ||
100 trust_level != web::URLVerificationTrustLevel::kAbsolute ||
101 !web::UrlHasWebScheme(page_url) || !web_state()->ContentIsHTML()) {
102 return;
103 }
104 [js_manager_ inject];
105 }
106
107 void CredentialManager::CredentialsRequested(
108 int request_id,
109 const GURL& source_url,
110 bool zero_click_only,
111 const std::vector<std::string>& federations,
112 bool is_user_initiated) {
113 // Invoked when the page invokes navigator.credentials.request(), this
114 // function will attempt to retrieve a Credential from the PasswordStore that
115 // meets the specified parameters and, if successful, send it back to the page
116 // via SendCredential.
117 DCHECK_GE(request_id, 0);
118 password_manager::PasswordStore* store = GetPasswordStore();
119
120 // If there's an outstanding request, or the PasswordStore isn't loaded yet,
121 // the request should fail outright and the JS Promise should be rejected
122 // with an appropriate error.
123 if (pending_request_ || !store) {
124 base::MessageLoop::current()->PostTask(
125 FROM_HERE,
126 base::Bind(&CredentialManager::RejectPromise,
127 weak_factory_.GetWeakPtr(), request_id,
128 pending_request_ ? ERROR_TYPE_PENDING_REQUEST
129 : ERROR_TYPE_PASSWORD_STORE_UNAVAILABLE));
130 return;
131 }
132
133 // If the page requested a zero-click credential -- one that can be returned
134 // without first asking the user -- and if zero-click isn't currently
135 // available, send back an empty credential.
136 if (zero_click_only && !IsZeroClickAllowed()) {
137 base::MessageLoop::current()->PostTask(
138 FROM_HERE, base::Bind(&CredentialManager::SendCredential,
139 weak_factory_.GetWeakPtr(), request_id,
140 password_manager::CredentialInfo()));
141 return;
142 }
143
144 // If the page origin is untrusted, the request should be rejected.
145 GURL page_url;
146 if (!GetUrlWithAbsoluteTrust(&page_url)) {
147 RejectPromise(request_id, ERROR_TYPE_SECURITY_ERROR_UNTRUSTED_ORIGIN);
148 return;
149 }
150
151 // Bundle up the arguments and forward them to the PasswordStore, which will
152 // asynchronously return the resulting Credential by invoking
153 // |SendCredential|.
154 std::vector<GURL> federation_urls;
155 for (const auto& federation : federations)
156 federation_urls.push_back(GURL(federation));
157 std::vector<std::string> realms;
158 pending_request_.reset(
159 new password_manager::CredentialManagerPendingRequestTask(
160 this, request_id, zero_click_only, page_url, federation_urls,
161 realms));
162 store->GetAutofillableLogins(pending_request_.get());
163 }
164
165 void CredentialManager::SignedIn(int request_id,
166 const GURL& source_url,
167 const web::Credential& credential) {
168 // Invoked when the page invokes navigator.credentials.notifySignedIn(), this
169 // function stores the signed-in |credential| and sends a message back to the
170 // page to resolve the Promise associated with |request_id|.
171 DCHECK(credential.type != web::CredentialType::CREDENTIAL_TYPE_EMPTY);
172 DCHECK_GE(request_id, 0);
173
174 // Requests from untrusted origins should be rejected.
175 GURL page_url;
176 if (!GetUrlWithAbsoluteTrust(&page_url)) {
177 RejectPromise(request_id, ERROR_TYPE_SECURITY_ERROR_UNTRUSTED_ORIGIN);
178 return;
179 }
180
181 // Notify the page that the notification was successful to avoid blocking the
182 // page while storing the credential. This is okay because the notification
183 // doesn't imply that the credential will be stored, just that it might be.
184 // It isn't the page's concern to know whether the storage took place or not.
185 ResolvePromise(request_id);
186
187 // Do nothing if the password manager isn't active.
188 if (!client_->IsSavingAndFillingEnabledForCurrentPage())
189 return;
190
191 // Store the signed-in credential so that the user can save it, if desired.
192 // Prompting the user and saving are handled by the PasswordFormManager.
193 scoped_ptr<autofill::PasswordForm> form(
194 password_manager::CreatePasswordFormFromCredentialInfo(
195 CredentialInfoFromWebCredential(credential), page_url));
196 form->skip_zero_click = !IsZeroClickAllowed();
197
198 // TODO(mkwst): This is a stub; we should be checking the PasswordStore to
199 // determine whether or not the credential exists, and calling UpdateLogin
200 // accordingly.
201 form_manager_.reset(
202 new password_manager::CredentialManagerPasswordFormManager(
203 client_, driver_->AsWeakPtr(), *form, this));
204 }
205
206 void CredentialManager::SignedOut(int request_id, const GURL& source_url) {
207 // Invoked when the page invokes navigator.credentials.notifySignedOut, this
208 // function notifies the PasswordStore that zero-click sign-in should be
209 // disabled for the current page origin.
210 DCHECK_GE(request_id, 0);
211
212 // Requests from untrusted origins should be rejected.
213 GURL page_url;
214 if (!GetUrlWithAbsoluteTrust(&page_url)) {
215 RejectPromise(request_id, ERROR_TYPE_SECURITY_ERROR_UNTRUSTED_ORIGIN);
216 return;
217 }
218
219 // The user signed out of the current page, so future zero-click credential
220 // requests for this page should fail: otherwise, the next time the user
221 // visits the page, if zero-click requests succeeded, the user might be auto-
222 // signed-in again with the credential that they just signed out. Forward this
223 // information to the PasswordStore via an asynchronous task.
224 password_manager::PasswordStore* store = GetPasswordStore();
225 if (store) {
226 // Bundle the origins that are sent to the PasswordStore if the task hasn't
227 // yet resolved. This task lives across page-loads to enable this bundling.
228 if (pending_require_user_mediation_) {
229 pending_require_user_mediation_->AddOrigin(page_url);
230 } else {
231 pending_require_user_mediation_.reset(
232 new password_manager::
233 CredentialManagerPendingRequireUserMediationTask(
234 this, page_url, std::vector<std::string>()));
235
236 // This will result in a callback to
237 // CredentialManagerPendingSignedOutTask::OnGetPasswordStoreResults().
238 store->GetAutofillableLogins(pending_require_user_mediation_.get());
239 }
240 }
241
242 // Acknowledge the page's signOut notification without waiting for the
243 // PasswordStore interaction to complete. The implementation of the sign-out
244 // notification isn't the page's concern.
245 ResolvePromise(request_id);
246 }
247
248 void CredentialManager::WebStateDestroyed() {
249 // When the WebState is destroyed, clean up everything that depends on it.
250 js_manager_.reset();
251 }
252
253 bool CredentialManager::IsZeroClickAllowed() const {
254 // Zero-click sign-in is only allowed when the user hasn't turned it off and
255 // when the user isn't in incognito mode.
256 return *zero_click_sign_in_enabled_ && !client_->IsOffTheRecord();
257 }
258
259 GURL CredentialManager::GetOrigin() const {
260 web::URLVerificationTrustLevel trust_level =
261 web::URLVerificationTrustLevel::kNone;
262 const GURL page_url(web_state()->GetCurrentURL(&trust_level));
263 DCHECK_EQ(trust_level, web::URLVerificationTrustLevel::kAbsolute);
264 return page_url;
265 }
266
267 void CredentialManager::SendCredential(
268 int request_id,
269 const password_manager::CredentialInfo& credential) {
270 // Invoked when the asynchronous interaction with the PasswordStore completes,
271 // this function forwards a |credential| back to the page via |js_manager_| by
272 // resolving the JavaScript Promise associated with |request_id|.
273 base::WeakPtr<CredentialManager> weak_this = weak_factory_.GetWeakPtr();
274 [js_manager_
275 resolvePromiseWithRequestID:request_id
276 credential:WebCredentialFromCredentialInfo(credential)
277 completionHandler:^(BOOL) {
278 if (weak_this)
279 weak_this->pending_request_.reset();
280 }];
281 }
282
283 password_manager::PasswordManagerClient* CredentialManager::client() const {
284 return client_;
285 }
286
287 autofill::PasswordForm CredentialManager::GetSynthesizedFormForOrigin() const {
288 autofill::PasswordForm synthetic_form;
289 synthetic_form.origin = web_state()->GetLastCommittedURL().GetOrigin();
290 synthetic_form.signon_realm = synthetic_form.origin.spec();
291 synthetic_form.scheme = autofill::PasswordForm::SCHEME_HTML;
292 synthetic_form.ssl_valid = synthetic_form.origin.SchemeIsCryptographic() &&
293 !client_->DidLastPageLoadEncounterSSLErrors();
294 return synthetic_form;
295 }
296
297 void CredentialManager::OnProvisionalSaveComplete() {
298 // Invoked after a credential sent up by the page was stored in a FormManager
299 // by |SignedIn|, this function asks the user if the password should be stored
300 // in the password manager.
301 DCHECK(form_manager_);
302 if (client_->IsSavingAndFillingEnabledForCurrentPage() &&
303 !form_manager_->IsBlacklisted()) {
304 client_->PromptUserToSaveOrUpdatePassword(
305 form_manager_.Pass(),
306 password_manager::CredentialSourceType::CREDENTIAL_SOURCE_API, false);
307 }
308 }
309
310 password_manager::PasswordStore* CredentialManager::GetPasswordStore() {
311 return client_ ? client_->GetPasswordStore() : nullptr;
312 }
313
314 void CredentialManager::DoneRequiringUserMediation() {
315 // Invoked when the PasswordStore has finished processing the list of origins
316 // that should have zero-click sign-in disabled.
317 DCHECK(pending_require_user_mediation_);
318 pending_require_user_mediation_.reset();
319 }
320
321 void CredentialManager::ResolvePromise(int request_id) {
322 [js_manager_ resolvePromiseWithRequestID:request_id completionHandler:nil];
323 }
324
325 void CredentialManager::RejectPromise(int request_id, ErrorType error_type) {
326 NSString* type = nil;
327 NSString* message = nil;
328 switch (error_type) {
329 case ERROR_TYPE_PENDING_REQUEST:
330 type = base::SysUTF8ToNSString(kCredentialsPendingRequestErrorType);
331 message = base::SysUTF8ToNSString(kCredentialsPendingRequestErrorMessage);
332 break;
333 case ERROR_TYPE_PASSWORD_STORE_UNAVAILABLE:
334 type = base::SysUTF8ToNSString(
335 kCredentialsPasswordStoreUnavailableErrorType);
336 message = base::SysUTF8ToNSString(
337 kCredentialsPasswordStoreUnavailableErrorMessage);
338 break;
339 case ERROR_TYPE_SECURITY_ERROR_UNTRUSTED_ORIGIN:
340 type = base::SysUTF8ToNSString(kCredentialsSecurityErrorType);
341 message = base::SysUTF8ToNSString(
342 kCredentialsSecurityErrorMessageUntrustedOrigin);
343 break;
344 }
345 [js_manager_ rejectPromiseWithRequestID:request_id
346 errorType:type
347 message:message
348 completionHandler:nil];
349 }
350
351 bool CredentialManager::GetUrlWithAbsoluteTrust(GURL* page_url) {
352 web::URLVerificationTrustLevel trust_level =
353 web::URLVerificationTrustLevel::kNone;
354 const GURL possibly_untrusted_url(web_state()->GetCurrentURL(&trust_level));
355 if (trust_level == web::URLVerificationTrustLevel::kAbsolute) {
356 *page_url = possibly_untrusted_url;
357 return true;
358 }
359 return false;
360 }
OLDNEW
« no previous file with comments | « ios/chrome/browser/passwords/credential_manager.h ('k') | ios/chrome/browser/passwords/js_credential_manager.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698