123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- // Copyright 2021 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #import "ios/components/credential_provider_extension/password_spec_fetcher.h"
- #include "base/base64.h"
- #include "components/autofill/core/browser/proto/password_requirements.pb.h"
- #if !defined(__has_feature) || !__has_feature(objc_arc)
- #error "This file requires ARC support."
- #endif
- using autofill::DomainSuggestions;
- using autofill::PasswordRequirementsSpec;
- namespace {
- // URL of the fetching endpoint.
- NSString* const kPasswordSpecURL =
- @"https://content-autofill.googleapis.com/v1/domainSuggestions/";
- // Header field name for the API key.
- NSString* const kApiKeyHeaderField = @"X-Goog-Api-Key";
- // Encoding requested from the server.
- NSString* const kEncodeKeyValue = @"base64";
- // Header field name for the encoding.
- NSString* const kEncodeKeyHeaderField = @"X-Goog-Encode-Response-If-Executable";
- // Query parameter name to for the type of response.
- NSString* const kAltQueryName = @"alt";
- // Query parameter value for a bits response (compared to a JSON response).
- NSString* const kAltQueryValue = @"proto";
- // Timeout for the spec fetching request.
- const NSTimeInterval kPasswordSpecTimeout = 10;
- }
- @interface PasswordSpecFetcher ()
- // Host that identifies the spec to be fetched.
- @property(nonatomic, copy) NSString* host;
- // API key to query the spec.
- @property(nonatomic, copy) NSString* APIKey;
- // Data task for fetching the spec.
- @property(nonatomic, copy) NSURLSessionDataTask* task;
- // Completion to be called once there is a response.
- @property(nonatomic, copy) FetchSpecCompletionBlock completion;
- // The spec if ready or an empty one if fetch hasn't happened.
- @property(nonatomic, readwrite) PasswordRequirementsSpec spec;
- @end
- @implementation PasswordSpecFetcher
- - (instancetype)initWithHost:(NSString*)host APIKey:(NSString*)APIKey {
- self = [super init];
- if (self) {
- // Replace a nil host with the empty string.
- if (!host) {
- host = @"";
- }
- _host = [host stringByAddingPercentEncodingWithAllowedCharacters:
- NSCharacterSet.URLQueryAllowedCharacterSet];
- _APIKey = APIKey;
- }
- return self;
- }
- - (BOOL)didFetchSpec {
- return self.task.state == NSURLSessionTaskStateCompleted;
- }
- - (void)fetchSpecWithCompletion:(FetchSpecCompletionBlock)completion {
- self.completion = completion;
- if (self.task) {
- return;
- }
- NSString* finalURL = [kPasswordSpecURL stringByAppendingString:self.host];
- NSURLComponents* URLComponents =
- [NSURLComponents componentsWithString:finalURL];
- NSURLQueryItem* queryAltItem =
- [NSURLQueryItem queryItemWithName:kAltQueryName value:kAltQueryValue];
- URLComponents.queryItems = @[ queryAltItem ];
- NSMutableURLRequest* request =
- [NSMutableURLRequest requestWithURL:URLComponents.URL
- cachePolicy:NSURLRequestUseProtocolCachePolicy
- timeoutInterval:kPasswordSpecTimeout];
- [request setValue:self.APIKey forHTTPHeaderField:kApiKeyHeaderField];
- [request setValue:kEncodeKeyValue forHTTPHeaderField:kEncodeKeyHeaderField];
- __weak __typeof__(self) weakSelf = self;
- NSURLSession* session = [NSURLSession sharedSession];
- self.task =
- [session dataTaskWithRequest:request
- completionHandler:^(NSData* data, NSURLResponse* response,
- NSError* error) {
- [weakSelf onReceivedData:data response:response error:error];
- }];
- [self.task resume];
- }
- - (void)onReceivedData:(NSData*)data
- response:(NSURLResponse*)response
- error:(NSError*)error {
- // Return early if there is an error.
- if (error) {
- [self executeCompletion];
- return;
- }
- // Parse the proto and execute completion.
- std::string decoded;
- const base::StringPiece encoded_bytes(static_cast<const char*>([data bytes]),
- [data length]);
- if (base::Base64Decode(encoded_bytes, &decoded)) {
- DomainSuggestions suggestions;
- suggestions.ParseFromString(decoded);
- if (suggestions.has_password_requirements()) {
- self.spec = suggestions.password_requirements();
- }
- }
- [self executeCompletion];
- }
- // Executes the completion if present. And releases it after.
- - (void)executeCompletion {
- if (self.completion) {
- auto completion = self.completion;
- self.completion = nil;
- dispatch_async(dispatch_get_main_queue(), ^{
- completion(self.spec);
- });
- }
- }
- @end
|