Commit 12482bcb authored by Fabian Sommer's avatar Fabian Sommer Committed by Commit Bot

Add Notifications for SecurityTokenSession

Add notification UI to SecurityTokenSessionController. These
notifications appear the first time a user was logged out / their
session was locked because of the SecurityTokenSessionBehavior policy.

A profile pref is used to save whether a user has already seen such a
notification once. If the session is locked, the controller creates
the notification directly. If the user is logged out, a local state pref
is used to schedule the creation of the notification.

Bug: 1131450
Change-Id: I27da14ec8d7a2fed53c46764ef64d156f399e86d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2505877Reviewed-by: default avatarDominic Battré <battre@chromium.org>
Reviewed-by: default avatarMaksim Ivanov <emaxx@chromium.org>
Reviewed-by: default avatarDenis Kuznetsov [CET] <antrim@chromium.org>
Commit-Queue: Fabian Sommer <fabiansommer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#827777}
parent 10b21b4a
...@@ -5958,5 +5958,14 @@ other {Your <ph name="DEVICE_TYPE">{1}<ex>Chromebook</ex></ph> will be locked au ...@@ -5958,5 +5958,14 @@ other {Your <ph name="DEVICE_TYPE">{1}<ex>Chromebook</ex></ph> will be locked au
Your <ph name="DEVICE_TYPE">{0}<ex>Chromebook</ex></ph> will be locked now. Your <ph name="DEVICE_TYPE">{0}<ex>Chromebook</ex></ph> will be locked now.
<ph name="DOMAIN">{1}<ex>example.com</ex></ph> requires you to keep your smart card inserted. <ph name="DOMAIN">{1}<ex>example.com</ex></ph> requires you to keep your smart card inserted.
</message> </message>
<message name="IDS_SECURITY_TOKEN_SESSION_LOGOUT_MESSAGE_TITLE" desc="This is the title of a notification appearing the first time a user is logged out because they removed their smart card. This only applies to users that authenticate with a smart card and for who this behavior was enabled by a policy.">
You were logged out.
</message>
<message name="IDS_SECURITY_TOKEN_SESSION_LOCK_MESSAGE_TITLE" desc="This is the title of a notification appearing the first time the session of a user is locked because they removed their smart card. This only applies to users that authenticate with a smart card and for who this behavior was enabled by a policy.">
Your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> was locked.
</message>
<message name="IDS_SECURITY_TOKEN_SESSION_LOGOUT_MESSAGE_BODY" desc="This is the text of a notification appearing the first time the a user was logged out because they removed their smart card. This only applies to users that authenticate with a smart card and for who this behavior was enabled by a policy.">
<ph name="DOMAIN">$1<ex>example.com</ex></ph> requires you to keep your smart card inserted.
</message>
</grit-part> </grit-part>
a1f9461d5574d2eff5164edb133c57052fc755f5
\ No newline at end of file
c9c4e8db94b73792a129ff57b8f1f846447e04be
\ No newline at end of file
c9c4e8db94b73792a129ff57b8f1f846447e04be
\ No newline at end of file
...@@ -4,11 +4,28 @@ ...@@ -4,11 +4,28 @@
#include "chrome/browser/chromeos/login/security_token_session_controller.h" #include "chrome/browser/chromeos/login/security_token_session_controller.h"
#include <string>
#include "ash/public/cpp/notification_utils.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/notifications/system_notification_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/pref_names.h" #include "chrome/common/pref_names.h"
#include "chrome/grit/generated_resources.h"
#include "chromeos/ui/vector_icons/vector_icons.h"
#include "components/prefs/pref_change_registrar.h" #include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h" #include "components/prefs/pref_service.h"
#include "components/user_manager/user.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/chromeos/devicetype_utils.h"
#include "ui/message_center/public/cpp/notification.h"
#include "url/gurl.h"
#include "url/url_constants.h"
namespace chromeos { namespace chromeos {
namespace login { namespace login {
...@@ -22,6 +39,11 @@ constexpr char kIgnorePrefValue[] = "IGNORE"; ...@@ -22,6 +39,11 @@ constexpr char kIgnorePrefValue[] = "IGNORE";
constexpr char kLogoutPrefValue[] = "LOGOUT"; constexpr char kLogoutPrefValue[] = "LOGOUT";
constexpr char kLockPrefValue[] = "LOCK"; constexpr char kLockPrefValue[] = "LOCK";
constexpr char kNotifierSecurityTokenSession[] =
"ash.security_token_session_controller";
constexpr char kNotificationId[] =
"security_token_session_controller_notification";
SecurityTokenSessionController::Behavior ParseBehaviorPrefValue( SecurityTokenSessionController::Behavior ParseBehaviorPrefValue(
const std::string& behavior) { const std::string& behavior) {
if (behavior == kIgnorePrefValue) if (behavior == kIgnorePrefValue)
...@@ -34,14 +56,67 @@ SecurityTokenSessionController::Behavior ParseBehaviorPrefValue( ...@@ -34,14 +56,67 @@ SecurityTokenSessionController::Behavior ParseBehaviorPrefValue(
return SecurityTokenSessionController::Behavior::kIgnore; return SecurityTokenSessionController::Behavior::kIgnore;
} }
std::string GetEnterpriseDomainFromEmail(const std::string& email) {
size_t email_separator_pos = email.find('@');
bool is_email = email_separator_pos != std::string::npos &&
email_separator_pos < email.length() - 1;
if (!is_email)
return std::string();
return gaia::ExtractDomainName(email);
}
// Checks if `domain` represents a valid domain. Returns false if `domain` is
// malformed. Returns the host part, which should be displayed to the user, in
// `sanitized_domain`.
bool SanitizeDomain(const std::string& domain, std::string& sanitized_domain) {
// Add "http://" to the url. Otherwise, "example.com" would be rejected,
// even though it has the format that is expected for `domain`.
GURL url(std::string(url::kHttpScheme) +
std::string(url::kStandardSchemeSeparator) + domain);
if (!url.is_valid())
return false;
if (!url.has_host())
return false;
sanitized_domain = url.host();
return true;
}
void DisplayNotification(const base::string16& title,
const base::string16& text) {
std::unique_ptr<message_center::Notification> notification =
ash::CreateSystemNotification(
message_center::NOTIFICATION_TYPE_SIMPLE, kNotificationId, title,
text,
/*display_source=*/base::string16(), /*origin_url=*/GURL(),
message_center::NotifierId(
message_center::NotifierType::SYSTEM_COMPONENT,
kNotifierSecurityTokenSession),
/*optional_fields=*/{},
new message_center::HandleNotificationClickDelegate(
base::DoNothing::Repeatedly()),
chromeos::kEnterpriseIcon,
message_center::SystemNotificationWarningLevel::NORMAL);
notification->set_fullscreen_visibility(
message_center::FullscreenVisibility::OVER_USER);
notification->SetSystemPriority();
SystemNotificationHelper::GetInstance()->Display(*notification);
}
} // namespace } // namespace
SecurityTokenSessionController::SecurityTokenSessionController( SecurityTokenSessionController::SecurityTokenSessionController(
PrefService* pref_service) PrefService* local_state,
: pref_service_(pref_service) { PrefService* profile_prefs,
const user_manager::User* user)
: local_state_(local_state), profile_prefs_(profile_prefs), user_(user) {
DCHECK(local_state_);
DCHECK(profile_prefs_);
DCHECK(user_);
UpdateNotificationPref(); UpdateNotificationPref();
UpdateBehaviorPref(); UpdateBehaviorPref();
pref_change_registrar_.Init(pref_service_); pref_change_registrar_.Init(profile_prefs_);
base::RepeatingClosure behavior_pref_changed_callback = base::RepeatingClosure behavior_pref_changed_callback =
base::BindRepeating(&SecurityTokenSessionController::UpdateBehaviorPref, base::BindRepeating(&SecurityTokenSessionController::UpdateBehaviorPref,
base::Unretained(this)); base::Unretained(this));
...@@ -62,12 +137,50 @@ void SecurityTokenSessionController::Shutdown() { ...@@ -62,12 +137,50 @@ void SecurityTokenSessionController::Shutdown() {
} }
// static // static
void SecurityTokenSessionController::RegisterPrefs( void SecurityTokenSessionController::RegisterLocalStatePrefs(
PrefRegistrySimple* registry) {
registry->RegisterStringPref(
prefs::kSecurityTokenSessionNotificationScheduledDomain, "");
}
// static
void SecurityTokenSessionController::RegisterProfilePrefs(
PrefRegistrySimple* registry) { PrefRegistrySimple* registry) {
registry->RegisterStringPref(prefs::kSecurityTokenSessionBehavior, registry->RegisterStringPref(prefs::kSecurityTokenSessionBehavior,
kIgnorePrefValue); kIgnorePrefValue);
registry->RegisterIntegerPref(prefs::kSecurityTokenSessionNotificationSeconds, registry->RegisterIntegerPref(prefs::kSecurityTokenSessionNotificationSeconds,
0); 0);
registry->RegisterBooleanPref(
prefs::kSecurityTokenSessionNotificationDisplayed, false);
}
// static
void SecurityTokenSessionController::MaybeDisplayLoginScreenNotification() {
PrefService* local_state = g_browser_process->local_state();
const PrefService::Preference* scheduled_notification_domain =
local_state->FindPreference(
prefs::kSecurityTokenSessionNotificationScheduledDomain);
if (!scheduled_notification_domain ||
scheduled_notification_domain->IsDefaultValue() ||
!scheduled_notification_domain->GetValue()->is_string()) {
// No notification is scheduled.
return;
}
local_state->ClearPref(
prefs::kSecurityTokenSessionNotificationScheduledDomain);
// Sanitize `scheduled_notification_domain`, as values coming from local state
// are not trusted.
std::string sanitized_domain;
if (!SanitizeDomain(scheduled_notification_domain->GetValue()->GetString(),
sanitized_domain)) {
// The pref value is invalid.
return;
}
DisplayNotification(
l10n_util::GetStringUTF16(
IDS_SECURITY_TOKEN_SESSION_LOGOUT_MESSAGE_TITLE),
l10n_util::GetStringFUTF16(IDS_SECURITY_TOKEN_SESSION_LOGOUT_MESSAGE_BODY,
base::UTF8ToUTF16(sanitized_domain)));
} }
void SecurityTokenSessionController::UpdateBehaviorPref() { void SecurityTokenSessionController::UpdateBehaviorPref() {
...@@ -76,14 +189,49 @@ void SecurityTokenSessionController::UpdateBehaviorPref() { ...@@ -76,14 +189,49 @@ void SecurityTokenSessionController::UpdateBehaviorPref() {
void SecurityTokenSessionController::UpdateNotificationPref() { void SecurityTokenSessionController::UpdateNotificationPref() {
notification_seconds_ = notification_seconds_ =
base::TimeDelta::FromSeconds(pref_service_->GetInteger( base::TimeDelta::FromSeconds(profile_prefs_->GetInteger(
prefs::kSecurityTokenSessionNotificationSeconds)); prefs::kSecurityTokenSessionNotificationSeconds));
} }
SecurityTokenSessionController::Behavior SecurityTokenSessionController::Behavior
SecurityTokenSessionController::GetBehaviorFromPref() const { SecurityTokenSessionController::GetBehaviorFromPref() const {
return ParseBehaviorPrefValue( return ParseBehaviorPrefValue(
pref_service_->GetString(prefs::kSecurityTokenSessionBehavior)); profile_prefs_->GetString(prefs::kSecurityTokenSessionBehavior));
}
void SecurityTokenSessionController::AddLockNotification() const {
// A user should see the notification only the first time their session is
// locked.
if (profile_prefs_->GetBoolean(
prefs::kSecurityTokenSessionNotificationDisplayed)) {
return;
}
profile_prefs_->SetBoolean(prefs::kSecurityTokenSessionNotificationDisplayed,
true);
std::string domain = GetEnterpriseDomainFromEmail(user_->GetDisplayEmail());
DisplayNotification(
l10n_util::GetStringFUTF16(IDS_SECURITY_TOKEN_SESSION_LOCK_MESSAGE_TITLE,
ui::GetChromeOSDeviceName()),
l10n_util::GetStringFUTF16(IDS_SECURITY_TOKEN_SESSION_LOGOUT_MESSAGE_BODY,
base::UTF8ToUTF16(domain)));
}
void SecurityTokenSessionController::ScheduleLogoutNotification() const {
// The notification can not be created directly, since it will not persist
// after the session is ended. Instead, use local state to schedule the
// creation of a notification.
if (profile_prefs_->GetBoolean(
prefs::kSecurityTokenSessionNotificationDisplayed)) {
// A user should see the notification only the first time they are logged
// out.
return;
}
profile_prefs_->SetBoolean(prefs::kSecurityTokenSessionNotificationDisplayed,
true);
local_state_->SetString(
prefs::kSecurityTokenSessionNotificationScheduledDomain,
GetEnterpriseDomainFromEmail(user_->GetDisplayEmail()));
} }
} // namespace login } // namespace login
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "components/prefs/pref_change_registrar.h" #include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h" #include "components/prefs/pref_service.h"
#include "components/user_manager/user.h"
namespace chromeos { namespace chromeos {
namespace login { namespace login {
...@@ -24,7 +25,9 @@ class SecurityTokenSessionController : public KeyedService { ...@@ -24,7 +25,9 @@ class SecurityTokenSessionController : public KeyedService {
public: public:
enum class Behavior { kIgnore, kLogout, kLock }; enum class Behavior { kIgnore, kLogout, kLock };
explicit SecurityTokenSessionController(PrefService* pref_service); SecurityTokenSessionController(PrefService* local_state,
PrefService* profile_prefs,
const user_manager::User* user);
SecurityTokenSessionController(const SecurityTokenSessionController& other) = SecurityTokenSessionController(const SecurityTokenSessionController& other) =
delete; delete;
SecurityTokenSessionController& operator=( SecurityTokenSessionController& operator=(
...@@ -34,15 +37,25 @@ class SecurityTokenSessionController : public KeyedService { ...@@ -34,15 +37,25 @@ class SecurityTokenSessionController : public KeyedService {
// KeyedService // KeyedService
void Shutdown() override; void Shutdown() override;
static void RegisterPrefs(PrefRegistrySimple* registry); static void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
static void RegisterProfilePrefs(PrefRegistrySimple* registry);
// If this controller logged the user out just before, display a notification
// explaining why this happened. This is only done the first time this
// happens for a user on a device.
static void MaybeDisplayLoginScreenNotification();
private: private:
Behavior GetBehaviorFromPref() const; Behavior GetBehaviorFromPref() const;
void UpdateBehaviorPref(); void UpdateBehaviorPref();
void UpdateNotificationPref(); void UpdateNotificationPref();
PrefService* const pref_service_; void AddLockNotification() const;
void ScheduleLogoutNotification() const;
PrefService* const local_state_;
PrefService* const profile_prefs_;
const user_manager::User* const user_;
PrefChangeRegistrar pref_change_registrar_; PrefChangeRegistrar pref_change_registrar_;
Behavior behavior_ = Behavior::kIgnore; Behavior behavior_ = Behavior::kIgnore;
base::TimeDelta notification_seconds_; base::TimeDelta notification_seconds_;
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "chrome/browser/chromeos/login/security_token_session_controller_factory.h" #include "chrome/browser/chromeos/login/security_token_session_controller_factory.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/certificate_provider/certificate_provider_service_factory.h" #include "chrome/browser/chromeos/certificate_provider/certificate_provider_service_factory.h"
#include "chrome/browser/chromeos/login/security_token_session_controller.h" #include "chrome/browser/chromeos/login/security_token_session_controller.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/browser/chromeos/profiles/profile_helper.h"
...@@ -46,7 +47,15 @@ KeyedService* SecurityTokenSessionControllerFactory::BuildServiceInstanceFor( ...@@ -46,7 +47,15 @@ KeyedService* SecurityTokenSessionControllerFactory::BuildServiceInstanceFor(
if (!chromeos::ProfileHelper::IsPrimaryProfile(profile)) if (!chromeos::ProfileHelper::IsPrimaryProfile(profile))
return nullptr; return nullptr;
return new SecurityTokenSessionController(profile->GetPrefs()); PrefService* local_state = g_browser_process->local_state();
if (!local_state) {
// This can happen in tests that do not have local state.
return nullptr;
}
user_manager::User* user = ProfileHelper::Get()->GetUserByProfile(
Profile::FromBrowserContext(context));
return new SecurityTokenSessionController(local_state, profile->GetPrefs(),
user);
} }
content::BrowserContext* content::BrowserContext*
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "chrome/browser/chromeos/login/reauth_stats.h" #include "chrome/browser/chromeos/login/reauth_stats.h"
#include "chrome/browser/chromeos/login/screens/chrome_user_selection_screen.h" #include "chrome/browser/chromeos/login/screens/chrome_user_selection_screen.h"
#include "chrome/browser/chromeos/login/screens/gaia_screen.h" #include "chrome/browser/chromeos/login/screens/gaia_screen.h"
#include "chrome/browser/chromeos/login/security_token_session_controller.h"
#include "chrome/browser/chromeos/login/ui/login_display.h" #include "chrome/browser/chromeos/login/ui/login_display.h"
#include "chrome/browser/chromeos/login/ui/login_display_mojo.h" #include "chrome/browser/chromeos/login/ui/login_display_mojo.h"
#include "chrome/browser/chromeos/login/user_board_view_mojo.h" #include "chrome/browser/chromeos/login/user_board_view_mojo.h"
...@@ -284,6 +285,8 @@ void LoginDisplayHostMojo::OnStartSignInScreen() { ...@@ -284,6 +285,8 @@ void LoginDisplayHostMojo::OnStartSignInScreen() {
UpdateAddUserButtonStatus(); UpdateAddUserButtonStatus();
OnStartSignInScreenCommon(); OnStartSignInScreenCommon();
login::SecurityTokenSessionController::MaybeDisplayLoginScreenNotification();
} }
void LoginDisplayHostMojo::OnPreferencesChanged() { void LoginDisplayHostMojo::OnPreferencesChanged() {
......
...@@ -687,6 +687,8 @@ void RegisterLocalState(PrefRegistrySimple* registry) { ...@@ -687,6 +687,8 @@ void RegisterLocalState(PrefRegistrySimple* registry) {
chromeos::language_prefs::RegisterPrefs(registry); chromeos::language_prefs::RegisterPrefs(registry);
chromeos::local_search_service::SearchMetricsReporterSync:: chromeos::local_search_service::SearchMetricsReporterSync::
RegisterLocalStatePrefs(registry); RegisterLocalStatePrefs(registry);
chromeos::login::SecurityTokenSessionController::RegisterLocalStatePrefs(
registry);
chromeos::MultiProfileUserController::RegisterPrefs(registry); chromeos::MultiProfileUserController::RegisterPrefs(registry);
chromeos::NetworkMetadataStore::RegisterPrefs(registry); chromeos::NetworkMetadataStore::RegisterPrefs(registry);
chromeos::NetworkThrottlingObserver::RegisterPrefs(registry); chromeos::NetworkThrottlingObserver::RegisterPrefs(registry);
...@@ -956,7 +958,8 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry, ...@@ -956,7 +958,8 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry,
chromeos::file_system_provider::RegisterProfilePrefs(registry); chromeos::file_system_provider::RegisterProfilePrefs(registry);
chromeos::full_restore::RegisterProfilePrefs(registry); chromeos::full_restore::RegisterProfilePrefs(registry);
chromeos::KerberosCredentialsManager::RegisterProfilePrefs(registry); chromeos::KerberosCredentialsManager::RegisterProfilePrefs(registry);
chromeos::login::SecurityTokenSessionController::RegisterPrefs(registry); chromeos::login::SecurityTokenSessionController::RegisterProfilePrefs(
registry);
chromeos::multidevice_setup::MultiDeviceSetupService::RegisterProfilePrefs( chromeos::multidevice_setup::MultiDeviceSetupService::RegisterProfilePrefs(
registry); registry);
chromeos::MultiProfileUserController::RegisterProfilePrefs(registry); chromeos::MultiProfileUserController::RegisterProfilePrefs(registry);
......
...@@ -3104,6 +3104,17 @@ const char kSecurityTokenSessionBehavior[] = "security_token_session_behavior"; ...@@ -3104,6 +3104,17 @@ const char kSecurityTokenSessionBehavior[] = "security_token_session_behavior";
// this pref is set to 0, the action happens immediately. // this pref is set to 0, the action happens immediately.
const char kSecurityTokenSessionNotificationSeconds[] = const char kSecurityTokenSessionNotificationSeconds[] =
"security_token_session_notification_seconds"; "security_token_session_notification_seconds";
// In addition to the notification described directly above, another
// notification will be displayed after the action happened. This only happens
// once for a user. This boolean pref saves whether this notification was
// already displayed for a user.
const char kSecurityTokenSessionNotificationDisplayed[] =
"security_token_session_notification_displayed";
// This string pref is set when the notification after the action mentioned
// above is about to be displayed. It contains the domain that manages the user
// who was logged out, to be used as part of the notification message.
const char kSecurityTokenSessionNotificationScheduledDomain[] =
"security_token_session_notification_scheduled";
#endif // defined(OS_CHROMEOS) #endif // defined(OS_CHROMEOS)
} // namespace prefs } // namespace prefs
...@@ -1087,6 +1087,8 @@ extern const char kLacrosAllowed[]; ...@@ -1087,6 +1087,8 @@ extern const char kLacrosAllowed[];
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
extern const char kSecurityTokenSessionBehavior[]; extern const char kSecurityTokenSessionBehavior[];
extern const char kSecurityTokenSessionNotificationSeconds[]; extern const char kSecurityTokenSessionNotificationSeconds[];
extern const char kSecurityTokenSessionNotificationDisplayed[];
extern const char kSecurityTokenSessionNotificationScheduledDomain[];
#endif #endif
} // namespace prefs } // namespace prefs
......
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