Commit 87e8c332 authored by Yicheng Li's avatar Yicheng Li Committed by Commit Bot

ash: Add InSessionAuthDialog class and a debug view

Add an InSessionAuthDialog class with a debug view, which currently
is a text prompt and two buttons. The InSessionAuthDialog will
show this debug view if the runtime flag show-auth-dialog-dev-overlay
is set, and will show nothing otherwise.

InSessionAuthDialog will be called by an AuthDialogController class
(to be added in a follow-up CL).ash: Add a barebone AuthenticationDialog class

Bug: b:156258540
Change-Id: Ib764399702ca557014b24faadf7855c4f9f39b07
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2298527
Commit-Queue: Yicheng Li <yichengli@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#792404}
parent 77b05efd
...@@ -458,6 +458,10 @@ component("ash") { ...@@ -458,6 +458,10 @@ component("ash") {
"ime/ime_switch_type.h", "ime/ime_switch_type.h",
"ime/mode_indicator_observer.cc", "ime/mode_indicator_observer.cc",
"ime/mode_indicator_observer.h", "ime/mode_indicator_observer.h",
"in_session_auth/auth_dialog_debug_view.cc",
"in_session_auth/auth_dialog_debug_view.h",
"in_session_auth/in_session_auth_dialog.cc",
"in_session_auth/in_session_auth_dialog.h",
"keyboard/arc/arc_virtual_keyboard_container_layout_manager.cc", "keyboard/arc/arc_virtual_keyboard_container_layout_manager.cc",
"keyboard/arc/arc_virtual_keyboard_container_layout_manager.h", "keyboard/arc/arc_virtual_keyboard_container_layout_manager.h",
"keyboard/keyboard_controller_impl.cc", "keyboard/keyboard_controller_impl.cc",
......
# In-Session User Authentication Dialog
This Ash dialog is for authenticating the user during a user session. The
primary use case is WebAuthn, where a platform daemon (u2fd) receives an
authentication request from the Chrome browser, and needs to initiate a user
authentication dialog which could involve fingerprint, PIN and password. More
authentication methods, such as SmartLock and smart cards, might be added in
the future.
This dialog is designed to be reused by other projects that need to trigger
in-session user authentication from ChromeOS, such as authenticating for ARC
apps.
This dialog is controlled by ash::AuthDialogController. When the user provides
a credential, the controller talks to cryptohome via
chromeos::ExtendedAuthenticator for authentication.
// Copyright 2020 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 "ash/in_session_auth/auth_dialog_debug_view.h"
#include <memory>
#include <utility>
#include "ash/login/ui/non_accessible_view.h"
#include "ash/login/ui/views_utils.h"
#include "base/strings/utf_string_conversions.h"
#include "ui/views/background.h"
#include "ui/views/controls/button/md_text_button.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/fill_layout.h"
namespace ash {
namespace {
enum class ButtonId {
kMoreOptions,
kCancel,
};
const char kTitle[] = "Verify it's you";
const char kFingerprintPrompt[] = "Touch the fingerprint sensor";
// If fingerprint option is available, password input field will be hidden
// until the user taps the MoreOptions button.
const char kMoreOptionsButtonText[] = "More options";
const char kCancelButtonText[] = "Cancel";
const int kContainerPreferredWidth = 512;
const int kTopVerticalSpacing = 24;
const int kVerticalSpacingBetweenTitleAndPrompt = 16;
const int kVerticalSpacingBetweenPromptAndButtons = 32;
const int kBottomVerticalSpacing = 20;
const int kButtonSpacing = 8;
const int kTitleFontSize = 14;
const int kPromptFontSize = 12;
} // namespace
AuthDialogDebugView::AuthDialogDebugView() {
SetLayoutManager(std::make_unique<views::FillLayout>());
container_ = AddChildView(std::make_unique<NonAccessibleView>());
container_->SetBackground(views::CreateSolidBackground(SK_ColorWHITE));
main_layout_ =
container_->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical));
main_layout_->set_main_axis_alignment(
views::BoxLayout::MainAxisAlignment::kStart);
main_layout_->set_cross_axis_alignment(
views::BoxLayout::CrossAxisAlignment::kStart);
AddVerticalSpacing(kTopVerticalSpacing);
AddTitleView();
AddVerticalSpacing(kVerticalSpacingBetweenTitleAndPrompt);
AddPromptView();
AddVerticalSpacing(kVerticalSpacingBetweenPromptAndButtons);
AddActionButtonsView();
AddVerticalSpacing(kBottomVerticalSpacing);
}
AuthDialogDebugView::~AuthDialogDebugView() = default;
void AuthDialogDebugView::AddTitleView() {
title_ = container_->AddChildView(std::make_unique<views::Label>());
title_->SetEnabledColor(SK_ColorBLACK);
title_->SetSubpixelRenderingEnabled(false);
title_->SetAutoColorReadabilityEnabled(false);
const gfx::FontList& base_font_list = views::Label::GetDefaultFontList();
title_->SetFontList(base_font_list.Derive(
kTitleFontSize, gfx::Font::FontStyle::NORMAL, gfx::Font::Weight::NORMAL));
title_->SetText(base::UTF8ToUTF16(kTitle));
title_->SetMaximumWidth(kContainerPreferredWidth);
title_->SetElideBehavior(gfx::ElideBehavior::ELIDE_TAIL);
}
void AuthDialogDebugView::AddPromptView() {
prompt_ = container_->AddChildView(std::make_unique<views::Label>());
prompt_->SetEnabledColor(SK_ColorBLACK);
prompt_->SetSubpixelRenderingEnabled(false);
prompt_->SetAutoColorReadabilityEnabled(false);
const gfx::FontList& base_font_list = views::Label::GetDefaultFontList();
prompt_->SetFontList(base_font_list.Derive(kPromptFontSize,
gfx::Font::FontStyle::NORMAL,
gfx::Font::Weight::NORMAL));
// TODO(yichengli): Use a different prompt if the board has no fingerprint
// sensor.
prompt_->SetText(base::UTF8ToUTF16(kFingerprintPrompt));
prompt_->SetMaximumWidth(kContainerPreferredWidth);
prompt_->SetElideBehavior(gfx::ElideBehavior::ELIDE_TAIL);
}
void AuthDialogDebugView::AddVerticalSpacing(int height) {
auto* spacing =
container_->AddChildView(std::make_unique<NonAccessibleView>());
spacing->SetPreferredSize(gfx::Size(kContainerPreferredWidth, height));
}
void AuthDialogDebugView::AddActionButtonsView() {
action_view_container_ =
container_->AddChildView(std::make_unique<NonAccessibleView>());
auto* buttons_layout = action_view_container_->SetLayoutManager(
std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal));
buttons_layout->set_between_child_spacing(kButtonSpacing);
more_options_button_ = AddButton(kMoreOptionsButtonText,
static_cast<int>(ButtonId::kMoreOptions),
action_view_container_);
cancel_button_ =
AddButton(kCancelButtonText, static_cast<int>(ButtonId::kCancel),
action_view_container_);
}
void AuthDialogDebugView::ButtonPressed(views::Button* sender,
const ui::Event& event) {
// TODO(yichengli): Enable cancel button to call AuthDialogController to
// destroy the dialog.
// TODO(yichengli): Enable more options button when we have both fingerprint
// view and password input view.
}
views::LabelButton* AuthDialogDebugView::AddButton(const std::string& text,
int id,
views::View* container) {
// Creates a button with |text|.
std::unique_ptr<views::LabelButton> button =
views::MdTextButton::Create(this, base::ASCIIToUTF16(text));
button->SetID(id);
views::LabelButton* view = button.get();
container->AddChildView(
login_views_utils::WrapViewForPreferredSize(std::move(button)));
return view;
}
} // namespace ash
// Copyright 2020 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.
#ifndef ASH_IN_SESSION_AUTH_AUTH_DIALOG_DEBUG_VIEW_H_
#define ASH_IN_SESSION_AUTH_AUTH_DIALOG_DEBUG_VIEW_H_
#include <string>
#include "ui/views/controls/button/button.h"
#include "ui/views/view.h"
namespace views {
class BoxLayout;
class Label;
class LabelButton;
} // namespace views
namespace ash {
// Contains the debug views that allows the developer to interact with the
// AuthDialogController.
class AuthDialogDebugView : public views::View, public views::ButtonListener {
public:
AuthDialogDebugView();
AuthDialogDebugView(const AuthDialogDebugView&) = delete;
AuthDialogDebugView& operator=(const AuthDialogDebugView&) = delete;
~AuthDialogDebugView() override;
// views::ButtonListener:
void ButtonPressed(views::Button* sender, const ui::Event& event) override;
private:
// Add a view for dialog title.
void AddTitleView();
// Add a view for the prompt message.
void AddPromptView();
// Add a vertical spacing view.
void AddVerticalSpacing(int height);
// Add a view for action buttons.
void AddActionButtonsView();
// Creates a button on the debug row that cannot be focused.
views::LabelButton* AddButton(const std::string& text,
int id,
views::View* container);
// Debug container which holds the entire debug UI.
views::View* container_ = nullptr;
// Layout for |container_|.
views::BoxLayout* main_layout_ = nullptr;
// Title of the auth dialog.
views::Label* title_ = nullptr;
// Prompt message to the user.
views::Label* prompt_ = nullptr;
// Show other authentication mechanisms if more than one.
views::LabelButton* more_options_button_ = nullptr;
// Cancel all operations and close th dialog.
views::LabelButton* cancel_button_ = nullptr;
// Container which holds action buttons.
views::View* action_view_container_ = nullptr;
};
} // namespace ash
#endif // ASH_IN_SESSION_AUTH_AUTH_DIALOG_DEBUG_VIEW_H_
// Copyright 2020 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 "ash/in_session_auth/in_session_auth_dialog.h"
#include "ash/in_session_auth/auth_dialog_debug_view.h"
#include "base/command_line.h"
#include "chromeos/constants/chromeos_switches.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
namespace ash {
namespace {
constexpr gfx::Size kDefaultSize(340, 224);
class AuthDialogWidgetDelegate : public views::WidgetDelegate {
public:
AuthDialogWidgetDelegate() = default;
AuthDialogWidgetDelegate(const AuthDialogWidgetDelegate&) = delete;
AuthDialogWidgetDelegate& operator=(const AuthDialogWidgetDelegate&) = delete;
~AuthDialogWidgetDelegate() override = default;
// views::WidgetDelegate:
views::View* GetInitiallyFocusedView() override {
return GetWidget()->GetContentsView();
}
void DeleteDelegate() override { delete this; }
ui::ModalType GetModalType() const override { return ui::MODAL_TYPE_SYSTEM; }
};
std::unique_ptr<views::Widget> CreateAuthDialogWidget(aura::Window* parent) {
views::Widget::InitParams params(
views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.delegate = new AuthDialogWidgetDelegate();
params.show_state = ui::SHOW_STATE_NORMAL;
params.parent = parent;
params.name = "AuthDialogWidget";
params.shadow_type = views::Widget::InitParams::ShadowType::kDrop;
params.shadow_elevation = 3;
gfx::Rect bounds = display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
bounds.ClampToCenteredSize(kDefaultSize);
params.bounds = bounds;
std::unique_ptr<views::Widget> widget = std::make_unique<views::Widget>();
widget->Init(std::move(params));
widget->SetVisibilityAnimationTransition(views::Widget::ANIMATE_NONE);
return widget;
}
} // namespace
InSessionAuthDialog::InSessionAuthDialog() {
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
chromeos::switches::kShowAuthDialogDevOverlay)) {
widget_ = CreateAuthDialogWidget(nullptr);
widget_->SetContentsView(std::make_unique<AuthDialogDebugView>());
widget_->Show();
}
}
InSessionAuthDialog::~InSessionAuthDialog() = default;
} // namespace ash
// Copyright 2020 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.
#ifndef ASH_IN_SESSION_AUTH_IN_SESSION_AUTH_DIALOG_H_
#define ASH_IN_SESSION_AUTH_IN_SESSION_AUTH_DIALOG_H_
#include <memory>
#include "ash/ash_export.h"
namespace views {
class Widget;
}
namespace ash {
// InSessionAuthDialog gets instantiated on every request to show
// an authentication dialog, and gets destroyed when the request is
// completed.
class InSessionAuthDialog {
public:
InSessionAuthDialog();
InSessionAuthDialog(const InSessionAuthDialog&) = delete;
InSessionAuthDialog& operator=(const InSessionAuthDialog&) = delete;
~InSessionAuthDialog();
views::Widget* widget() { return widget_.get(); }
bool is_shown() const { return !!widget_; }
private:
// The dialog widget. Owned by this class so that we can close the widget
// when auth completes.
std::unique_ptr<views::Widget> widget_;
};
} // namespace ash
#endif // ASH_IN_SESSION_AUTH_IN_SESSION_AUTH_DIALOG_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment