gdm: Integrate username asking into the ShellUserVerifier flow
Currently, prompting for the username on the login screen is done "out-of-band". This leads to subtle bugs where we're trying to integrate two different state machines, or interpret results from one state machine as part of another. It also complicates the logic, as the ability to know whether we need or want a username is currently in the UI layer rather than the logic layer. Move this into the verifier proper.
This commit is contained in:
parent
056375cbcf
commit
fb824131ae
@ -24,11 +24,6 @@ const AuthPromptMode = {
|
||||
UNLOCK_OR_LOG_IN: 1
|
||||
};
|
||||
|
||||
const BeginRequestType = {
|
||||
PROVIDE_USERNAME: 0,
|
||||
DONT_PROVIDE_USERNAME: 1
|
||||
};
|
||||
|
||||
const AuthPrompt = new Lang.Class({
|
||||
Name: 'AuthPrompt',
|
||||
|
||||
@ -44,6 +39,7 @@ const AuthPrompt = new Lang.Class({
|
||||
|
||||
this._userVerifier = new GdmUtil.ShellUserVerifier(this._gdmClient, { reauthenticationOnly: reauthenticationOnly });
|
||||
|
||||
this._userVerifier.connect('needs-username', Lang.bind(this, this._onNeedsUserName));
|
||||
this._userVerifier.connect('ask-question', Lang.bind(this, this._onAskQuestion));
|
||||
this._userVerifier.connect('show-message', Lang.bind(this, this._onShowMessage));
|
||||
this._userVerifier.connect('verification-failed', Lang.bind(this, this._onVerificationFailed));
|
||||
@ -186,6 +182,14 @@ const AuthPrompt = new Lang.Class({
|
||||
}));
|
||||
},
|
||||
|
||||
_onNeedsUserName: function() {
|
||||
this.emit('needs-username');
|
||||
},
|
||||
|
||||
gotUserName: function(userName) {
|
||||
this._userVerifier.gotUserName(userName);
|
||||
},
|
||||
|
||||
_onAskQuestion: function(verifier, serviceName, question, passwordChar) {
|
||||
if (this._preemptiveAnswer) {
|
||||
if (this._queryingService)
|
||||
@ -403,24 +407,7 @@ const AuthPrompt = new Lang.Class({
|
||||
this.setUser(null);
|
||||
this.stopSpinning();
|
||||
|
||||
let beginRequestType;
|
||||
|
||||
if (this._mode == AuthPromptMode.UNLOCK_ONLY) {
|
||||
// The user is constant at the unlock screen, so it will immediately
|
||||
// respond to the request with the username
|
||||
beginRequestType = BeginRequestType.PROVIDE_USERNAME;
|
||||
} else if (this._userVerifier.serviceIsForeground(GdmUtil.OVIRT_SERVICE_NAME) ||
|
||||
(this.smartcardDetected &&
|
||||
this._userVerifier.serviceIsForeground(GdmUtil.SMARTCARD_SERVICE_NAME))) {
|
||||
// We don't need to know the username if the user preempted the login screen
|
||||
// with a smartcard or with preauthenticated oVirt credentials
|
||||
beginRequestType = BeginRequestType.DONT_PROVIDE_USERNAME;
|
||||
} else {
|
||||
// In all other cases, we should get the username up front.
|
||||
beginRequestType = BeginRequestType.PROVIDE_USERNAME;
|
||||
}
|
||||
|
||||
this.emit('reset', beginRequestType);
|
||||
this.emit('reset');
|
||||
},
|
||||
|
||||
addCharacter: function(unichar) {
|
||||
@ -431,9 +418,12 @@ const AuthPrompt = new Lang.Class({
|
||||
this._entry.clutter_text.insert_unichar(unichar);
|
||||
},
|
||||
|
||||
begin: function(userName) {
|
||||
begin: function() {
|
||||
this.updateSensitivity(false);
|
||||
this._userVerifier.begin();
|
||||
},
|
||||
|
||||
needsUsername: function() {
|
||||
this._userVerifier.begin(userName);
|
||||
},
|
||||
|
||||
|
@ -421,6 +421,7 @@ const LoginDialog = new Lang.Class({
|
||||
this._authPrompt = new AuthPrompt.AuthPrompt(gdmClient, AuthPrompt.AuthPromptMode.UNLOCK_OR_LOG_IN);
|
||||
this._authPrompt.connect('prompted', Lang.bind(this, this._onPrompted));
|
||||
this._authPrompt.connect('reset', Lang.bind(this, this._onReset));
|
||||
this._authPrompt.connect('needs-username', Lang.bind(this, this._onNeedsUserName));
|
||||
this._authPrompt.hide();
|
||||
this.actor.add_child(this._authPrompt.actor);
|
||||
|
||||
@ -469,14 +470,13 @@ const LoginDialog = new Lang.Class({
|
||||
this._sessionMenuButton.actor.show();
|
||||
this._authPrompt.addActorToDefaultButtonWell(this._sessionMenuButton.actor);
|
||||
|
||||
this._disableUserList = undefined;
|
||||
this._updateDisableUserList();
|
||||
this._userListLoaded = false;
|
||||
|
||||
// If the user list is enabled, it should take key focus; make sure the
|
||||
// screen shield is initialized first to prevent it from stealing the
|
||||
// focus later
|
||||
Main.layoutManager.connect('startup-complete',
|
||||
Lang.bind(this, this._updateDisableUserList));
|
||||
Main.layoutManager.connect('startup-complete', Lang.bind(this, this._reset));
|
||||
},
|
||||
|
||||
_ensureUserListLoaded: function() {
|
||||
@ -493,15 +493,20 @@ const LoginDialog = new Lang.Class({
|
||||
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, this._loadUserList));
|
||||
},
|
||||
|
||||
_reset: function() {
|
||||
this._authPrompt.reset();
|
||||
this._authPrompt.begin();
|
||||
},
|
||||
|
||||
_updateDisableUserList: function() {
|
||||
let disableUserList = this._settings.get_boolean(GdmUtil.DISABLE_USER_LIST_KEY);
|
||||
if (disableUserList == this._disableUserList)
|
||||
return;
|
||||
|
||||
if (disableUserList != this._disableUserList) {
|
||||
this._disableUserList = disableUserList;
|
||||
this._disableUserList = disableUserList;
|
||||
|
||||
if (this._authPrompt.verificationStatus == GdmUtil.VerificationStatus.NOT_VERIFYING)
|
||||
this._authPrompt.reset();
|
||||
}
|
||||
if (this._authPrompt.verificationStatus == GdmUtil.VerificationStatus.ASKING_FOR_USERNAME)
|
||||
this._reset();
|
||||
},
|
||||
|
||||
_updateCancelButton: function() {
|
||||
@ -509,7 +514,7 @@ const LoginDialog = new Lang.Class({
|
||||
|
||||
// Hide the cancel button if the user list is disabled and we're asking for
|
||||
// a username
|
||||
if (this._authPrompt.verificationStatus == GdmUtil.VerificationStatus.NOT_VERIFYING && this._disableUserList)
|
||||
if (this._authPrompt.verificationStatus == GdmUtil.VerificationStatus.ASKING_FOR_USERNAME && this._disableUserList)
|
||||
cancelVisible = false;
|
||||
else
|
||||
cancelVisible = true;
|
||||
@ -554,19 +559,18 @@ const LoginDialog = new Lang.Class({
|
||||
this._showPrompt();
|
||||
},
|
||||
|
||||
_onReset: function(authPrompt, beginRequest) {
|
||||
_onReset: function() {
|
||||
this._sessionMenuButton.updateSensitivity(true);
|
||||
|
||||
this._user = null;
|
||||
|
||||
if (beginRequest == AuthPrompt.BeginRequestType.PROVIDE_USERNAME) {
|
||||
if (!this._disableUserList)
|
||||
this._showUserList();
|
||||
else
|
||||
this._hideUserListAskForUsernameAndBeginVerification();
|
||||
} else {
|
||||
this._hideUserListAndBeginVerification();
|
||||
}
|
||||
this._reset();
|
||||
},
|
||||
|
||||
_onNeedsUserName: function() {
|
||||
if (!this._disableUserList)
|
||||
this._showUserList();
|
||||
else
|
||||
this._hideUserListAskForUsernameAndBeginVerification();
|
||||
},
|
||||
|
||||
_onDefaultSessionChanged: function(client, sessionId) {
|
||||
@ -625,7 +629,7 @@ const LoginDialog = new Lang.Class({
|
||||
this._user = this._userManager.get_user(answer);
|
||||
this._authPrompt.clear();
|
||||
this._authPrompt.startSpinning();
|
||||
this._authPrompt.begin(answer);
|
||||
this._authPrompt.gotUserName(answer);
|
||||
this._updateCancelButton();
|
||||
|
||||
realmManager.disconnect(realmSignalId)
|
||||
@ -805,11 +809,6 @@ const LoginDialog = new Lang.Class({
|
||||
this._askForUsernameAndBeginVerification();
|
||||
},
|
||||
|
||||
_hideUserListAndBeginVerification: function() {
|
||||
this._hideUserList();
|
||||
this._authPrompt.begin();
|
||||
},
|
||||
|
||||
_showUserList: function() {
|
||||
this._ensureUserListLoaded();
|
||||
this._authPrompt.hide();
|
||||
@ -823,7 +822,7 @@ const LoginDialog = new Lang.Class({
|
||||
this._authPrompt.setUser(item.user);
|
||||
|
||||
let userName = item.user.get_user_name();
|
||||
this._authPrompt.begin(userName);
|
||||
this._authPrompt.gotUserName(userName);
|
||||
},
|
||||
|
||||
_onUserListActivated: function(activatedItem) {
|
||||
|
@ -121,9 +121,10 @@ function cloneAndFadeOutActor(actor) {
|
||||
|
||||
const VerificationStatus = {
|
||||
NOT_VERIFYING: 0,
|
||||
VERIFYING: 1,
|
||||
VERIFICATION_FAILED: 2,
|
||||
VERIFICATION_SUCCEEDED: 3
|
||||
ASKING_FOR_USERNAME: 1,
|
||||
VERIFYING: 2,
|
||||
VERIFICATION_FAILED: 3,
|
||||
VERIFICATION_SUCCEEDED: 4,
|
||||
};
|
||||
|
||||
const ShellUserVerifier = new Lang.Class({
|
||||
@ -169,24 +170,52 @@ const ShellUserVerifier = new Lang.Class({
|
||||
_reset: function() {
|
||||
// Clear previous attempts to authenticate
|
||||
this.verificationStatus = VerificationStatus.NOT_VERIFYING;
|
||||
this._userName = null;
|
||||
this._failCounter = 0;
|
||||
this._updateDefaultService();
|
||||
this.emit('reset');
|
||||
},
|
||||
|
||||
begin: function(userName) {
|
||||
this.verificationStatus = VerificationStatus.VERIFYING;
|
||||
begin: function() {
|
||||
if (this._mode == AuthPromptMode.UNLOCK_ONLY) {
|
||||
// The user is constant at the unlock screen, so it will immediately
|
||||
// respond to the request with the username
|
||||
needsUsername = true;
|
||||
} else if (this.serviceIsForeground(GdmUtil.OVIRT_SERVICE_NAME) ||
|
||||
(this.smartcardDetected &&
|
||||
this.serviceIsForeground(GdmUtil.SMARTCARD_SERVICE_NAME))) {
|
||||
// We don't need to know the username if the user preempted the login screen
|
||||
// with a smartcard or with preauthenticated oVirt credentials
|
||||
needsUsername = false;
|
||||
} else {
|
||||
// In all other cases, we should get the username up front.
|
||||
needsUsername = true;
|
||||
}
|
||||
|
||||
this._cancellable = new Gio.Cancellable();
|
||||
if (needsUsername) {
|
||||
this.verificationStatus = VerificationStatus.ASKING_FOR_USERNAME;
|
||||
this.emit('needs-username');
|
||||
} else {
|
||||
this._beginAuthentication();
|
||||
}
|
||||
},
|
||||
|
||||
gotUserName: function(userName) {
|
||||
this._userName = userName;
|
||||
this._beginAuthentication();
|
||||
},
|
||||
|
||||
_beginAuthentication: function() {
|
||||
this.verificationStatus = VerificationStatus.VERIFYING;
|
||||
this._cancellable = new Gio.Cancellable();
|
||||
this.reauthenticating = false;
|
||||
|
||||
this._checkForFingerprintReader();
|
||||
|
||||
if (userName) {
|
||||
if (this._userName) {
|
||||
// If possible, reauthenticate an already running session,
|
||||
// so any session specific credentials get updated appropriately
|
||||
this._client.open_reauthentication_channel(userName, this._cancellable,
|
||||
this._client.open_reauthentication_channel(this._userName, this._cancellable,
|
||||
Lang.bind(this, this._reauthenticationChannelOpened));
|
||||
} else {
|
||||
this._client.get_user_verifier(this._cancellable, Lang.bind(this, this._userVerifierGot));
|
||||
@ -501,10 +530,6 @@ const ShellUserVerifier = new Lang.Class({
|
||||
this.verificationStatus = VerificationStatus.VERIFICATION_SUCCEEDED;
|
||||
},
|
||||
|
||||
_retry: function() {
|
||||
this.begin(this._userName);
|
||||
},
|
||||
|
||||
_verificationFailed: function(retry) {
|
||||
// For Not Listed / enterprise logins, immediately reset
|
||||
// the dialog
|
||||
@ -520,7 +545,7 @@ const ShellUserVerifier = new Lang.Class({
|
||||
|
||||
this._doAfterPendingMessages(Lang.bind(this, function() {
|
||||
if (canRetry)
|
||||
this._retry();
|
||||
this._beginAuthentication();
|
||||
else
|
||||
this.clear();
|
||||
}));
|
||||
|
@ -54,6 +54,7 @@ const UnlockDialog = new Lang.Class({
|
||||
this._authPrompt = new AuthPrompt.AuthPrompt(new Gdm.Client(), AuthPrompt.AuthPromptMode.UNLOCK_ONLY);
|
||||
this._authPrompt.connect('failed', Lang.bind(this, this._fail));
|
||||
this._authPrompt.connect('reset', Lang.bind(this, this._onReset));
|
||||
this._authPrompt.connect('needs-username', Lang.bind(this, this._onNeedsUserName));
|
||||
this._authPrompt.setPasswordChar('\u25cf');
|
||||
this._authPrompt.nextButton.label = _("Unlock");
|
||||
|
||||
@ -100,6 +101,10 @@ const UnlockDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
_onReset: function(authPrompt, beginRequest) {
|
||||
this._authPrompt.begin();
|
||||
},
|
||||
|
||||
_onNeedsUserName: function() {
|
||||
let userName;
|
||||
if (beginRequest == AuthPrompt.BeginRequestType.PROVIDE_USERNAME) {
|
||||
this._authPrompt.setUser(this._user);
|
||||
@ -108,7 +113,7 @@ const UnlockDialog = new Lang.Class({
|
||||
userName = null;
|
||||
}
|
||||
|
||||
this._authPrompt.begin(userName);
|
||||
this._authPrompt.gotUserName(userName);
|
||||
},
|
||||
|
||||
_escape: function() {
|
||||
|
Loading…
Reference in New Issue
Block a user