1// Copyright (c) 2012 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 "chrome/browser/chromeos/enrollment_dialog_view.h"
6
7#include "base/bind.h"
8#include "base/strings/utf_string_conversions.h"
9#include "chrome/browser/chromeos/profiles/profile_helper.h"
10#include "chrome/browser/profiles/profile.h"
11#include "chrome/browser/profiles/profile_manager.h"
12#include "chrome/browser/ui/browser_finder.h"
13#include "chrome/browser/ui/browser_navigator.h"
14#include "chrome/grit/generated_resources.h"
15#include "chromeos/network/client_cert_util.h"
16#include "chromeos/network/managed_network_configuration_handler.h"
17#include "chromeos/network/network_event_log.h"
18#include "chromeos/network/network_state.h"
19#include "chromeos/network/network_state_handler.h"
20#include "extensions/browser/extension_host.h"
21#include "extensions/common/constants.h"
22#include "ui/base/l10n/l10n_util.h"
23#include "ui/base/page_transition_types.h"
24#include "ui/views/controls/label.h"
25#include "ui/views/layout/grid_layout.h"
26#include "ui/views/layout/layout_constants.h"
27#include "ui/views/widget/widget.h"
28#include "ui/views/window/dialog_delegate.h"
29
30namespace chromeos {
31
32namespace {
33
34// Default width/height of the dialog.
35const int kDefaultWidth = 350;
36const int kDefaultHeight = 100;
37
38////////////////////////////////////////////////////////////////////////////////
39// Dialog for certificate enrollment. This displays the content from the
40// certificate enrollment URI.
41class EnrollmentDialogView : public views::DialogDelegateView {
42 public:
43  virtual ~EnrollmentDialogView();
44
45  static void ShowDialog(gfx::NativeWindow owning_window,
46                         const std::string& network_name,
47                         Profile* profile,
48                         const GURL& target_uri,
49                         const base::Closure& connect);
50
51  // views::DialogDelegateView overrides
52  virtual int GetDialogButtons() const OVERRIDE;
53  virtual bool Accept() OVERRIDE;
54  virtual void OnClosed() OVERRIDE;
55  virtual base::string16 GetDialogButtonLabel(
56      ui::DialogButton button) const OVERRIDE;
57
58  // views::WidgetDelegate overrides
59  virtual ui::ModalType GetModalType() const OVERRIDE;
60  virtual base::string16 GetWindowTitle() const OVERRIDE;
61
62  // views::View overrides
63  virtual gfx::Size GetPreferredSize() const OVERRIDE;
64
65 private:
66  EnrollmentDialogView(const std::string& network_name,
67                       Profile* profile,
68                       const GURL& target_uri,
69                       const base::Closure& connect);
70  void InitDialog();
71
72  bool accepted_;
73  std::string network_name_;
74  Profile* profile_;
75  GURL target_uri_;
76  base::Closure connect_;
77  bool added_cert_;
78};
79
80////////////////////////////////////////////////////////////////////////////////
81// EnrollmentDialogView implementation.
82
83EnrollmentDialogView::EnrollmentDialogView(const std::string& network_name,
84                                           Profile* profile,
85                                           const GURL& target_uri,
86                                           const base::Closure& connect)
87    : accepted_(false),
88      network_name_(network_name),
89      profile_(profile),
90      target_uri_(target_uri),
91      connect_(connect),
92      added_cert_(false) {
93}
94
95EnrollmentDialogView::~EnrollmentDialogView() {
96}
97
98// static
99void EnrollmentDialogView::ShowDialog(gfx::NativeWindow owning_window,
100                                      const std::string& network_name,
101                                      Profile* profile,
102                                      const GURL& target_uri,
103                                      const base::Closure& connect) {
104  EnrollmentDialogView* dialog_view =
105      new EnrollmentDialogView(network_name, profile, target_uri, connect);
106  views::DialogDelegate::CreateDialogWidget(dialog_view, NULL, owning_window);
107  dialog_view->InitDialog();
108  views::Widget* widget = dialog_view->GetWidget();
109  DCHECK(widget);
110  widget->Show();
111}
112
113int EnrollmentDialogView::GetDialogButtons() const {
114  return ui::DIALOG_BUTTON_CANCEL | ui::DIALOG_BUTTON_OK;
115}
116
117bool EnrollmentDialogView::Accept() {
118  accepted_ = true;
119  return true;
120}
121
122void EnrollmentDialogView::OnClosed() {
123  if (!accepted_)
124    return;
125  chrome::NavigateParams params(profile_,
126                                GURL(target_uri_),
127                                ui::PAGE_TRANSITION_LINK);
128  params.disposition = NEW_FOREGROUND_TAB;
129  params.window_action = chrome::NavigateParams::SHOW_WINDOW;
130  chrome::Navigate(&params);
131}
132
133base::string16 EnrollmentDialogView::GetDialogButtonLabel(
134    ui::DialogButton button) const {
135  if (button == ui::DIALOG_BUTTON_OK)
136    return l10n_util::GetStringUTF16(IDS_NETWORK_ENROLLMENT_HANDLER_BUTTON);
137  return views::DialogDelegateView::GetDialogButtonLabel(button);
138}
139
140ui::ModalType EnrollmentDialogView::GetModalType() const {
141  return ui::MODAL_TYPE_SYSTEM;
142}
143
144base::string16 EnrollmentDialogView::GetWindowTitle() const {
145  return l10n_util::GetStringUTF16(IDS_NETWORK_ENROLLMENT_HANDLER_TITLE);
146}
147
148gfx::Size EnrollmentDialogView::GetPreferredSize() const {
149  return gfx::Size(kDefaultWidth, kDefaultHeight);
150}
151
152void EnrollmentDialogView::InitDialog() {
153  added_cert_ = false;
154  // Create the views and layout manager and set them up.
155  views::Label* label = new views::Label(
156      l10n_util::GetStringFUTF16(IDS_NETWORK_ENROLLMENT_HANDLER_INSTRUCTIONS,
157                                 base::UTF8ToUTF16(network_name_)));
158  label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
159  label->SetMultiLine(true);
160  label->SetAllowCharacterBreak(true);
161
162  views::GridLayout* grid_layout = views::GridLayout::CreatePanel(this);
163  SetLayoutManager(grid_layout);
164
165  views::ColumnSet* columns = grid_layout->AddColumnSet(0);
166  columns->AddColumn(views::GridLayout::FILL,  // Horizontal resize.
167                     views::GridLayout::FILL,  // Vertical resize.
168                     1,   // Resize weight.
169                     views::GridLayout::USE_PREF,  // Size type.
170                     0,   // Ignored for USE_PREF.
171                     0);  // Minimum size.
172  columns = grid_layout->AddColumnSet(1);
173  columns->AddPaddingColumn(
174      0, views::kUnrelatedControlHorizontalSpacing);
175  columns->AddColumn(views::GridLayout::LEADING,  // Horizontal leading.
176                     views::GridLayout::FILL,     // Vertical resize.
177                     1,   // Resize weight.
178                     views::GridLayout::USE_PREF,  // Size type.
179                     0,   // Ignored for USE_PREF.
180                     0);  // Minimum size.
181
182  grid_layout->StartRow(0, 0);
183  grid_layout->AddView(label);
184  grid_layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing);
185  grid_layout->Layout(this);
186}
187
188////////////////////////////////////////////////////////////////////////////////
189// Handler for certificate enrollment.
190
191class DialogEnrollmentDelegate {
192 public:
193  // |owning_window| is the window that will own the dialog.
194  DialogEnrollmentDelegate(gfx::NativeWindow owning_window,
195                           const std::string& network_name,
196                           Profile* profile);
197  ~DialogEnrollmentDelegate();
198
199  // EnrollmentDelegate overrides
200  bool Enroll(const std::vector<std::string>& uri_list,
201              const base::Closure& connect);
202
203 private:
204  gfx::NativeWindow owning_window_;
205  std::string network_name_;
206  Profile* profile_;
207
208  DISALLOW_COPY_AND_ASSIGN(DialogEnrollmentDelegate);
209};
210
211DialogEnrollmentDelegate::DialogEnrollmentDelegate(
212    gfx::NativeWindow owning_window,
213    const std::string& network_name,
214    Profile* profile) : owning_window_(owning_window),
215                        network_name_(network_name),
216                        profile_(profile) {}
217
218DialogEnrollmentDelegate::~DialogEnrollmentDelegate() {}
219
220bool DialogEnrollmentDelegate::Enroll(const std::vector<std::string>& uri_list,
221                                      const base::Closure& post_action) {
222  // Keep the closure for later activation if we notice that
223  // a certificate has been added.
224
225  // TODO(gspencer): Do something smart with the closure.  At the moment it is
226  // being ignored because we don't know when the enrollment tab is closed.
227  // http://crosbug.com/30422
228  for (std::vector<std::string>::const_iterator iter = uri_list.begin();
229       iter != uri_list.end(); ++iter) {
230    GURL uri(*iter);
231    if (uri.IsStandard() || uri.scheme() == extensions::kExtensionScheme) {
232      // If this is a "standard" scheme, like http, ftp, etc., then open that in
233      // the enrollment dialog.
234      NET_LOG_EVENT("Showing enrollment dialog", network_name_);
235      EnrollmentDialogView::ShowDialog(owning_window_,
236                                       network_name_,
237                                       profile_,
238                                       uri, post_action);
239      return true;
240    }
241    NET_LOG_DEBUG("Nonstandard URI: " + uri.spec(), network_name_);
242  }
243
244  // No appropriate scheme was found.
245  NET_LOG_ERROR("No usable enrollment URI", network_name_);
246  return false;
247}
248
249void EnrollmentComplete(const std::string& service_path) {
250  NET_LOG_USER("Enrollment Complete", service_path);
251}
252
253}  // namespace
254
255////////////////////////////////////////////////////////////////////////////////
256// Factory function.
257
258namespace enrollment {
259
260bool CreateDialog(const std::string& service_path,
261                  gfx::NativeWindow owning_window) {
262  const NetworkState* network = NetworkHandler::Get()->network_state_handler()->
263      GetNetworkState(service_path);
264  if (!network) {
265    NET_LOG_ERROR("Enrolling Unknown network", service_path);
266    return false;
267  }
268  Browser* browser = chrome::FindBrowserWithWindow(owning_window);
269  Profile* profile =
270      browser ? browser->profile() : ProfileManager::GetPrimaryUserProfile();
271  std::string username_hash = ProfileHelper::GetUserIdHashFromProfile(profile);
272
273  onc::ONCSource onc_source = onc::ONC_SOURCE_NONE;
274  const base::DictionaryValue* policy =
275      NetworkHandler::Get()
276          ->managed_network_configuration_handler()
277          ->FindPolicyByGUID(username_hash, network->guid(), &onc_source);
278
279  // We skip certificate patterns for device policy ONC so that an unmanaged
280  // user can't get to the place where a cert is presented for them
281  // involuntarily.
282  if (!policy || onc_source == onc::ONC_SOURCE_DEVICE_POLICY)
283    return false;
284
285  client_cert::ClientCertConfig cert_config;
286  OncToClientCertConfig(*policy, &cert_config);
287
288  if (cert_config.client_cert_type != onc::client_cert::kPattern)
289    return false;
290
291  if (cert_config.pattern.Empty())
292    NET_LOG_ERROR("Certificate pattern is empty", service_path);
293
294  if (cert_config.pattern.enrollment_uri_list().empty()) {
295    NET_LOG_EVENT("No enrollment URIs", service_path);
296    return false;
297  }
298
299  NET_LOG_USER("Enrolling", service_path);
300
301  DialogEnrollmentDelegate* enrollment =
302      new DialogEnrollmentDelegate(owning_window, network->name(), profile);
303  return enrollment->Enroll(cert_config.pattern.enrollment_uri_list(),
304                            base::Bind(&EnrollmentComplete, service_path));
305}
306
307}  // namespace enrollment
308
309}  // namespace chromeos
310