root/remoting/host/token_validator_base.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. weak_factory_
  2. ValidateThirdPartyToken
  3. token_url
  4. token_scope
  5. OnResponseStarted
  6. OnReadCompleted
  7. OnCertificateRequested
  8. OnCertificatesSelected
  9. IsValidScope
  10. ProcessResponse

// Copyright 2014 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.

#include "remoting/host/token_validator_base.h"

#include "base/base64.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/json/json_reader.h"
#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "net/base/escape.h"
#include "net/base/io_buffer.h"
#include "net/base/request_priority.h"
#include "net/base/upload_bytes_element_reader.h"
#include "net/base/upload_data_stream.h"
#include "net/ssl/client_cert_store.h"
#if defined(USE_NSS)
#include "net/ssl/client_cert_store_nss.h"
#elif defined(OS_WIN)
#include "net/ssl/client_cert_store_win.h"
#elif defined(OS_MACOSX)
#include "net/ssl/client_cert_store_mac.h"
#endif
#include "net/ssl/ssl_cert_request_info.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_status.h"
#include "url/gurl.h"

namespace {

const int kBufferSize = 4096;
const char kCertIssuerWildCard[] = "*";

}  // namespace

namespace remoting {

TokenValidatorBase::TokenValidatorBase(
    const ThirdPartyAuthConfig& third_party_auth_config,
    const std::string& token_scope,
    scoped_refptr<net::URLRequestContextGetter> request_context_getter)
    : third_party_auth_config_(third_party_auth_config),
      token_scope_(token_scope),
      request_context_getter_(request_context_getter),
      buffer_(new net::IOBuffer(kBufferSize)),
      weak_factory_(this) {
  DCHECK(third_party_auth_config_.token_url.is_valid());
  DCHECK(third_party_auth_config_.token_validation_url.is_valid());
}

TokenValidatorBase::~TokenValidatorBase() {
}

// TokenValidator interface.
void TokenValidatorBase::ValidateThirdPartyToken(
    const std::string& token,
    const base::Callback<void(
        const std::string& shared_secret)>& on_token_validated) {
  DCHECK(!request_);
  DCHECK(!on_token_validated.is_null());

  on_token_validated_ = on_token_validated;

  StartValidateRequest(token);
}

const GURL& TokenValidatorBase::token_url() const {
  return third_party_auth_config_.token_url;
}

const std::string& TokenValidatorBase::token_scope() const {
  return token_scope_;
}

// URLFetcherDelegate interface.
void TokenValidatorBase::OnResponseStarted(net::URLRequest* source) {
  DCHECK_EQ(request_.get(), source);

  int bytes_read = 0;
  request_->Read(buffer_.get(), kBufferSize, &bytes_read);
  OnReadCompleted(request_.get(), bytes_read);
}

void TokenValidatorBase::OnReadCompleted(net::URLRequest* source,
                                         int bytes_read) {
  DCHECK_EQ(request_.get(), source);

  do {
    if (!request_->status().is_success() || bytes_read <= 0)
      break;

    data_.append(buffer_->data(), bytes_read);
  } while (request_->Read(buffer_.get(), kBufferSize, &bytes_read));

  const net::URLRequestStatus status = request_->status();

  if (!status.is_io_pending()) {
    std::string shared_token = ProcessResponse();
    request_.reset();
    on_token_validated_.Run(shared_token);
  }
}

void TokenValidatorBase::OnCertificateRequested(
    net::URLRequest* source,
    net::SSLCertRequestInfo* cert_request_info) {
  DCHECK_EQ(request_.get(), source);

  net::ClientCertStore* client_cert_store;
#if defined(USE_NSS)
  client_cert_store = new net::ClientCertStoreNSS(
      net::ClientCertStoreNSS::PasswordDelegateFactory());
#elif defined(OS_WIN)
  client_cert_store = new net::ClientCertStoreWin();
#elif defined(OS_MACOSX)
  client_cert_store = new net::ClientCertStoreMac();
#else
#error Unknown platform.
#endif
  // The callback is uncancellable, and GetClientCert requires selected_certs
  // and client_cert_store to stay alive until the callback is called. So we
  // must give it a WeakPtr for |this|, and ownership of the other parameters.
  net::CertificateList* selected_certs(new net::CertificateList());
  client_cert_store->GetClientCerts(
      *cert_request_info, selected_certs,
      base::Bind(&TokenValidatorBase::OnCertificatesSelected,
                 weak_factory_.GetWeakPtr(), base::Owned(selected_certs),
                 base::Owned(client_cert_store)));
}

void TokenValidatorBase::OnCertificatesSelected(
    net::CertificateList* selected_certs,
    net::ClientCertStore* unused) {
  const std::string& issuer =
      third_party_auth_config_.token_validation_cert_issuer;
  if (request_) {
    for (size_t i = 0; i < selected_certs->size(); ++i) {
      if (issuer == kCertIssuerWildCard ||
          issuer == (*selected_certs)[i]->issuer().common_name) {
        request_->ContinueWithCertificate((*selected_certs)[i]);
        return;
      }
    }
    request_->ContinueWithCertificate(NULL);
  }
}

bool TokenValidatorBase::IsValidScope(const std::string& token_scope) {
  // TODO(rmsousa): Deal with reordering/subsets/supersets/aliases/etc.
  return token_scope == token_scope_;
}

std::string TokenValidatorBase::ProcessResponse() {
  // Verify that we got a successful response.
  net::URLRequestStatus status = request_->status();
  if (!status.is_success()) {
    LOG(ERROR) << "Error validating token, status=" << status.status()
               << " err=" << status.error();
    return std::string();
  }

  int response = request_->GetResponseCode();
  if (response != 200) {
    LOG(ERROR)
        << "Error " << response << " validating token: '" << data_ << "'";
    return std::string();
  }

  // Decode the JSON data from the response.
  scoped_ptr<base::Value> value(base::JSONReader::Read(data_));
  base::DictionaryValue* dict;
  if (!value.get() || value->GetType() != base::Value::TYPE_DICTIONARY ||
      !value->GetAsDictionary(&dict)) {
    LOG(ERROR) << "Invalid token validation response: '" << data_ << "'";
    return std::string();
  }

  std::string token_scope;
  dict->GetStringWithoutPathExpansion("scope", &token_scope);
  if (!IsValidScope(token_scope)) {
    LOG(ERROR) << "Invalid scope: '" << token_scope
               << "', expected: '" << token_scope_ <<"'.";
    return std::string();
  }

  std::string shared_secret;
  // Everything is valid, so return the shared secret to the caller.
  dict->GetStringWithoutPathExpansion("access_token", &shared_secret);
  return shared_secret;
}

}  // namespace remoting

/* [<][>][^][v][top][bottom][index][help] */