Compare commits

...

20 Commits

Author SHA1 Message Date
Jasper St. Pierre
28f4030aa2 retrigger greeter 2014-03-07 19:36:31 -05:00
Jasper St. Pierre
67fe376564 gdm: Make the smartcard and service status fields private
We don't need to export these publicly any more.
2014-03-07 19:36:31 -05:00
Jasper St. Pierre
fb824131ae 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.
2014-03-07 19:36:18 -05:00
Jasper St. Pierre
056375cbcf gdm: Merge clear() and cancel()
cancel() isn't used anymore.
2014-03-07 18:49:41 -05:00
Jasper St. Pierre
84431cbc65 gdm: Move reset-on-smartcard / reset-on-ovirt code to ShellUserVerifier
We still need to expose smartcardDetected to determine whether to
prompt for a username. This part is another hairy mess of signals
and layer violations that will need untangling, but we'll get there...
2014-03-07 18:47:37 -05:00
Jasper St. Pierre
92c283da4d gdm: Move the verificationStatus state machine to ShellUserVerifier
The idea here is that the ShellUserVerifier does all the logic of
identifying the user, and the AuthPrompt is simply the UI on top
of it.

Thus, it makes sense for more of the state machine to be driven by
the ShellUserVerifier.

This will become more apparent in the next commit.
2014-03-07 18:47:37 -05:00
Jasper St. Pierre
6e27ef8ff9 gdm: Emit the 'failed' status immediately, and not on reset
There's really no reason to do it this way: it works in both cases,
and it's a lot simpler to reason about.
2014-03-07 18:47:37 -05:00
Jasper St. Pierre
0c511c884b gdm: Remove the (partially) unused cancelled signal
When we emit the 'cancelled' signal if we were previously verifying,
we'll always emit a 'failed' signal. LoginDialog doesn't care about
cancelled, only UnlockDialog cares about both, but it does the same
thing for both, so there's no reason to have the extra signal.
2014-03-07 18:47:37 -05:00
Jasper St. Pierre
7afb503666 gdm: Rearrange code to make it slightly easier to read 2014-03-07 18:47:37 -05:00
Jasper St. Pierre
0b9d01a1c0 gdm: Merge some duplicate code
Do a reset at initialization time.

Technically, this isn't needed, since to start the process we'll need
to reset(); anyway after all the signals have connected, but this helps
the code be clean.
2014-03-07 18:47:37 -05:00
Jasper St. Pierre
b978d99820 gdm: Remove params from AuthPrompt.begin();
It's just one parameter now.
2014-03-07 18:47:36 -05:00
Jasper St. Pierre
6d8d094e0c gdm: Remove the hold from AuthPrompt / UserVerifier
After the removal before, it's now unused.
2014-03-07 18:47:36 -05:00
Jasper St. Pierre
97e0175f48 gdm: Don't bother passing a Hold through to the authPrompt
beginVerificationForUser is always the last task in the last, so it
doesn't matter when it's released.
2014-03-07 18:47:36 -05:00
Jasper St. Pierre
ebef4ff174 gdm: Clean up pending messages handling
Introduce a new doAfterPendingMessages helper to wait until
we have no more pending messages before doing an action.
2014-03-07 18:47:36 -05:00
Jasper St. Pierre
82b8b32355 gdm: Correct placement of verification-failed emission
If we fail to verify, and don't have any pending messages, we'll retry
or cancel, which will cause a state change to VERIFYING or NOT_VERIFYING,
and then change the state again to VERIFICATION_FAILED, messing up our
state machine.
2014-03-07 18:47:35 -05:00
Jasper St. Pierre
5b4337f716 gdm: Remove _cancelAndReset
Calling Cancel(); on the UserVerifier will make us get a reset
signal eventually, so simply wait for the natural flow.
2014-03-07 18:42:17 -05:00
Jasper St. Pierre
5eb377bd3b gdm: Replace preemptingService with checks in getForegroundService
This removes a piece of the chicken-wire state machine to determine
what the current foreground service should be.
2014-03-07 18:42:17 -05:00
Jasper St. Pierre
8173110842 gdm: Make a method private 2014-03-07 18:42:17 -05:00
Jasper St. Pierre
fee97da26b activate session 2014-03-07 18:42:07 -05:00
Jasper St. Pierre
0cc10e0c5d mutter-launch was killed upstream 2014-03-07 18:42:07 -05:00
7 changed files with 202 additions and 223 deletions

View File

@ -2,7 +2,7 @@
Type=Application Type=Application
_Name=GNOME Shell (wayland compositor) _Name=GNOME Shell (wayland compositor)
_Comment=Window management and application launching _Comment=Window management and application launching
Exec=@bindir@/mutter-launch -- gnome-shell-wayland --wayland Exec=@bindir@/gnome-shell-wayland --wayland --display-server
X-GNOME-Bugzilla-Bugzilla=GNOME X-GNOME-Bugzilla-Bugzilla=GNOME
X-GNOME-Bugzilla-Product=gnome-shell X-GNOME-Bugzilla-Product=gnome-shell
X-GNOME-Bugzilla-Component=general X-GNOME-Bugzilla-Component=general

View File

@ -24,24 +24,10 @@ const AuthPromptMode = {
UNLOCK_OR_LOG_IN: 1 UNLOCK_OR_LOG_IN: 1
}; };
const AuthPromptStatus = {
NOT_VERIFYING: 0,
VERIFYING: 1,
VERIFICATION_FAILED: 2,
VERIFICATION_SUCCEEDED: 3
};
const BeginRequestType = {
PROVIDE_USERNAME: 0,
DONT_PROVIDE_USERNAME: 1
};
const AuthPrompt = new Lang.Class({ const AuthPrompt = new Lang.Class({
Name: 'AuthPrompt', Name: 'AuthPrompt',
_init: function(gdmClient, mode) { _init: function(gdmClient, mode) {
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
this._gdmClient = gdmClient; this._gdmClient = gdmClient;
this._mode = mode; this._mode = mode;
@ -53,14 +39,11 @@ const AuthPrompt = new Lang.Class({
this._userVerifier = new GdmUtil.ShellUserVerifier(this._gdmClient, { reauthenticationOnly: reauthenticationOnly }); 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('ask-question', Lang.bind(this, this._onAskQuestion));
this._userVerifier.connect('show-message', Lang.bind(this, this._onShowMessage)); this._userVerifier.connect('show-message', Lang.bind(this, this._onShowMessage));
this._userVerifier.connect('verification-failed', Lang.bind(this, this._onVerificationFailed)); this._userVerifier.connect('verification-failed', Lang.bind(this, this._onVerificationFailed));
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
this._userVerifier.connect('reset', Lang.bind(this, this._onReset)); this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
this._userVerifier.connect('smartcard-status-changed', Lang.bind(this, this._onSmartcardStatusChanged));
this._userVerifier.connect('ovirt-user-authenticated', Lang.bind(this, this._onOVirtUserAuthenticated));
this.smartcardDetected = this._userVerifier.smartcardDetected;
this.connect('next', Lang.bind(this, function() { this.connect('next', Lang.bind(this, function() {
this.updateSensitivity(false); this.updateSensitivity(false);
@ -133,6 +116,10 @@ const AuthPrompt = new Lang.Class({
this._defaultButtonWell.add_child(this._spinner.actor); this._defaultButtonWell.add_child(this._spinner.actor);
}, },
get verificationStatus() {
return this._userVerifier.verificationStatus;
},
_onDestroy: function() { _onDestroy: function() {
this._userVerifier.clear(); this._userVerifier.clear();
this._userVerifier.disconnectAll(); this._userVerifier.disconnectAll();
@ -193,6 +180,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) { _onAskQuestion: function(verifier, serviceName, question, passwordChar) {
if (this._preemptiveAnswer) { if (this._preemptiveAnswer) {
if (this._queryingService) if (this._queryingService)
@ -221,30 +216,6 @@ const AuthPrompt = new Lang.Class({
this.emit('prompted'); this.emit('prompted');
}, },
_onOVirtUserAuthenticated: function() {
if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED)
this.reset();
},
_onSmartcardStatusChanged: function() {
this.smartcardDetected = this._userVerifier.smartcardDetected;
// Most of the time we want to reset if the user inserts or removes
// a smartcard. Smartcard insertion "preempts" what the user was
// doing, and smartcard removal aborts the preemption.
// The exceptions are: 1) Don't reset on smartcard insertion if we're already verifying
// with a smartcard
// 2) Don't reset if we've already succeeded at verification and
// the user is getting logged in.
if (this._userVerifier.serviceIsDefault(GdmUtil.SMARTCARD_SERVICE_NAME) &&
this.verificationStatus == AuthPromptStatus.VERIFYING &&
this.smartcardDetected)
return;
if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED)
this.reset();
},
_onShowMessage: function(userVerifier, message, type) { _onShowMessage: function(userVerifier, message, type) {
this.setMessage(message, type); this.setMessage(message, type);
this.emit('prompted'); this.emit('prompted');
@ -256,15 +227,11 @@ const AuthPrompt = new Lang.Class({
this.updateSensitivity(true); this.updateSensitivity(true);
this.setActorInDefaultButtonWell(null); this.setActorInDefaultButtonWell(null);
this.verificationStatus = AuthPromptStatus.VERIFICATION_FAILED;
},
_onVerificationComplete: function() { this.emit('failed');
this.verificationStatus = AuthPromptStatus.VERIFICATION_SUCCEEDED;
}, },
_onReset: function() { _onReset: function() {
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
this.reset(); this.reset();
}, },
@ -428,39 +395,13 @@ const AuthPrompt = new Lang.Class({
}, },
reset: function() { reset: function() {
let oldStatus = this.verificationStatus;
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
if (oldStatus == AuthPromptStatus.VERIFYING)
this._userVerifier.cancel();
this._queryingService = null; this._queryingService = null;
this.clear(); this.clear();
this._message.opacity = 0; this._message.opacity = 0;
this.setUser(null); this.setUser(null);
this.stopSpinning(); this.stopSpinning();
if (oldStatus == AuthPromptStatus.VERIFICATION_FAILED) this.emit('reset');
this.emit('failed');
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);
}, },
addCharacter: function(unichar) { addCharacter: function(unichar) {
@ -471,18 +412,13 @@ const AuthPrompt = new Lang.Class({
this._entry.clutter_text.insert_unichar(unichar); this._entry.clutter_text.insert_unichar(unichar);
}, },
begin: function(params) { begin: function() {
params = Params.parse(params, { userName: null,
hold: null });
this.updateSensitivity(false); this.updateSensitivity(false);
this._userVerifier.begin();
},
let hold = params.hold; needsUsername: function() {
if (!hold) this._userVerifier.begin(userName);
hold = new Batch.Hold();
this._userVerifier.begin(params.userName, hold);
this.verificationStatus = AuthPromptStatus.VERIFYING;
}, },
finish: function(onComplete) { finish: function(onComplete) {
@ -500,7 +436,6 @@ const AuthPrompt = new Lang.Class({
cancel: function() { cancel: function() {
this.reset(); this.reset();
this.emit('cancelled');
} }
}); });
Signals.addSignalMethods(AuthPrompt.prototype); Signals.addSignalMethods(AuthPrompt.prototype);

View File

@ -421,6 +421,7 @@ const LoginDialog = new Lang.Class({
this._authPrompt = new AuthPrompt.AuthPrompt(gdmClient, AuthPrompt.AuthPromptMode.UNLOCK_OR_LOG_IN); this._authPrompt = new AuthPrompt.AuthPrompt(gdmClient, AuthPrompt.AuthPromptMode.UNLOCK_OR_LOG_IN);
this._authPrompt.connect('prompted', Lang.bind(this, this._onPrompted)); this._authPrompt.connect('prompted', Lang.bind(this, this._onPrompted));
this._authPrompt.connect('reset', Lang.bind(this, this._onReset)); this._authPrompt.connect('reset', Lang.bind(this, this._onReset));
this._authPrompt.connect('needs-username', Lang.bind(this, this._onNeedsUserName));
this._authPrompt.hide(); this._authPrompt.hide();
this.actor.add_child(this._authPrompt.actor); this.actor.add_child(this._authPrompt.actor);
@ -469,14 +470,15 @@ const LoginDialog = new Lang.Class({
this._sessionMenuButton.actor.show(); this._sessionMenuButton.actor.show();
this._authPrompt.addActorToDefaultButtonWell(this._sessionMenuButton.actor); this._authPrompt.addActorToDefaultButtonWell(this._sessionMenuButton.actor);
this._disableUserList = undefined; this._updateDisableUserList();
this._userListLoaded = false; this._userListLoaded = false;
LoginManager.getLoginManager().getCurrentSessionProxy(Lang.bind(this, this._gotGreeterSessionProxy));
// If the user list is enabled, it should take key focus; make sure the // 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 // screen shield is initialized first to prevent it from stealing the
// focus later // focus later
Main.layoutManager.connect('startup-complete', Main.layoutManager.connect('startup-complete', Lang.bind(this, this._reset));
Lang.bind(this, this._updateDisableUserList));
}, },
_ensureUserListLoaded: function() { _ensureUserListLoaded: function() {
@ -493,15 +495,20 @@ const LoginDialog = new Lang.Class({
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, this._loadUserList)); GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, this._loadUserList));
}, },
_reset: function() {
this._authPrompt.reset();
this._authPrompt.begin();
},
_updateDisableUserList: function() { _updateDisableUserList: function() {
let disableUserList = this._settings.get_boolean(GdmUtil.DISABLE_USER_LIST_KEY); 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 == AuthPrompt.AuthPromptStatus.NOT_VERIFYING) if (this._authPrompt.verificationStatus == GdmUtil.VerificationStatus.ASKING_FOR_USERNAME)
this._authPrompt.reset(); this._reset();
}
}, },
_updateCancelButton: function() { _updateCancelButton: function() {
@ -509,7 +516,7 @@ const LoginDialog = new Lang.Class({
// Hide the cancel button if the user list is disabled and we're asking for // Hide the cancel button if the user list is disabled and we're asking for
// a username // a username
if (this._authPrompt.verificationStatus == AuthPrompt.AuthPromptStatus.NOT_VERIFYING && this._disableUserList) if (this._authPrompt.verificationStatus == GdmUtil.VerificationStatus.ASKING_FOR_USERNAME && this._disableUserList)
cancelVisible = false; cancelVisible = false;
else else
cancelVisible = true; cancelVisible = true;
@ -554,19 +561,18 @@ const LoginDialog = new Lang.Class({
this._showPrompt(); this._showPrompt();
}, },
_onReset: function(authPrompt, beginRequest) { _onReset: function() {
this._sessionMenuButton.updateSensitivity(true); this._sessionMenuButton.updateSensitivity(true);
this._user = null; this._user = null;
if (beginRequest == AuthPrompt.BeginRequestType.PROVIDE_USERNAME) { this._reset();
if (!this._disableUserList) },
this._showUserList();
else _onNeedsUserName: function() {
this._hideUserListAskForUsernameAndBeginVerification(); if (!this._disableUserList)
} else { this._showUserList();
this._hideUserListAndBeginVerification(); else
} this._hideUserListAskForUsernameAndBeginVerification();
}, },
_onDefaultSessionChanged: function(client, sessionId) { _onDefaultSessionChanged: function(client, sessionId) {
@ -574,8 +580,8 @@ const LoginDialog = new Lang.Class({
}, },
_shouldShowSessionMenuButton: function() { _shouldShowSessionMenuButton: function() {
if (this._authPrompt.verificationStatus != AuthPrompt.AuthPromptStatus.VERIFYING && if (this._authPrompt.verificationStatus != GdmUtil.VerificationStatus.VERIFYING &&
this._authPrompt.verificationStatus != AuthPrompt.AuthPromptStatus.VERIFICATION_FAILED) this._authPrompt.verificationStatus != GdmUtil.VerificationStatus.VERIFICATION_FAILED)
return false; return false;
if (this._user && this._user.is_loaded && this._user.is_logged_in()) if (this._user && this._user.is_loaded && this._user.is_logged_in())
@ -625,7 +631,7 @@ const LoginDialog = new Lang.Class({
this._user = this._userManager.get_user(answer); this._user = this._userManager.get_user(answer);
this._authPrompt.clear(); this._authPrompt.clear();
this._authPrompt.startSpinning(); this._authPrompt.startSpinning();
this._authPrompt.begin({ userName: answer }); this._authPrompt.gotUserName(answer);
this._updateCancelButton(); this._updateCancelButton();
realmManager.disconnect(realmSignalId) realmManager.disconnect(realmSignalId)
@ -635,6 +641,32 @@ const LoginDialog = new Lang.Class({
this._showPrompt(); this._showPrompt();
}, },
_sessionActivated: function() {
// We fade out the shell after logging in, and then re-set
// the greeter wen we're VT switched to again.
// XXX: re-trigger startup animation
if (this._authPrompt.verificationStatus == GdmUtil.VerificationStatus.VERIFICATION_SUCCEEDED) {
this._reset();
// XXX: do something better here
this.actor.opacity = 255;
let children = Main.layoutManager.uiGroup.get_children();
for (let i = 0; i < children.length; i++) {
if (children[i] != Main.layoutManager.screenShieldGroup)
children[i].opacity = 255;
}
}
},
_gotGreeterSessionProxy: function(proxy) {
proxy.connect('g-properties-changed', Lang.bind(this, function() {
if (proxy.Active)
this._sessionActivated();
}));
},
_startSession: function(serviceName) { _startSession: function(serviceName) {
Tweener.addTween(this.actor, Tweener.addTween(this.actor,
{ opacity: 0, { opacity: 0,
@ -805,11 +837,6 @@ const LoginDialog = new Lang.Class({
this._askForUsernameAndBeginVerification(); this._askForUsernameAndBeginVerification();
}, },
_hideUserListAndBeginVerification: function() {
this._hideUserList();
this._authPrompt.begin();
},
_showUserList: function() { _showUserList: function() {
this._ensureUserListLoaded(); this._ensureUserListLoaded();
this._authPrompt.hide(); this._authPrompt.hide();
@ -823,11 +850,7 @@ const LoginDialog = new Lang.Class({
this._authPrompt.setUser(item.user); this._authPrompt.setUser(item.user);
let userName = item.user.get_user_name(); let userName = item.user.get_user_name();
let hold = new Batch.Hold(); this._authPrompt.gotUserName(userName);
this._authPrompt.begin({ userName: userName,
hold: hold });
return hold;
}, },
_onUserListActivated: function(activatedItem) { _onUserListActivated: function(activatedItem) {
@ -842,9 +865,9 @@ const LoginDialog = new Lang.Class({
this._updateCancelButton(); this._updateCancelButton();
let batch = new Batch.ConcurrentBatch(this, [new Batch.ConsecutiveBatch(this, tasks), let batch = new Batch.ConsecutiveBatch(this, tasks);
this._beginVerificationForItem(activatedItem)]);
batch.run(); batch.run();
this._beginVerificationForItem(activatedItem);
}, },
_onDestroy: function() { _onDestroy: function() {

View File

@ -119,6 +119,14 @@ function cloneAndFadeOutActor(actor) {
return hold; return hold;
} }
const VerificationStatus = {
NOT_VERIFYING: 0,
ASKING_FOR_USERNAME: 1,
VERIFYING: 2,
VERIFICATION_FAILED: 3,
VERIFICATION_SUCCEEDED: 4,
};
const ShellUserVerifier = new Lang.Class({ const ShellUserVerifier = new Lang.Class({
Name: 'ShellUserVerifier', Name: 'ShellUserVerifier',
@ -131,7 +139,6 @@ const ShellUserVerifier = new Lang.Class({
this._settings = new Gio.Settings({ schema: LOGIN_SCREEN_SCHEMA }); this._settings = new Gio.Settings({ schema: LOGIN_SCREEN_SCHEMA });
this._settings.connect('changed', this._settings.connect('changed',
Lang.bind(this, this._updateDefaultService)); Lang.bind(this, this._updateDefaultService));
this._updateDefaultService();
this._fprintManager = new Fprint.FprintManager(); this._fprintManager = new Fprint.FprintManager();
this._smartcardManager = SmartcardManager.getSmartcardManager(); this._smartcardManager = SmartcardManager.getSmartcardManager();
@ -152,45 +159,69 @@ const ShellUserVerifier = new Lang.Class({
this.hasPendingMessages = false; this.hasPendingMessages = false;
this.reauthenticating = false; this.reauthenticating = false;
this._failCounter = 0;
this._oVirtCredentialsManager = OVirt.getOVirtCredentialsManager(); this._oVirtCredentialsManager = OVirt.getOVirtCredentialsManager();
this._oVirtCredentialsManager.connect('user-authenticated', Lang.bind(this, this._oVirtUserAuthenticated));
if (this._oVirtCredentialsManager.hasToken()) if (this._oVirtCredentialsManager.hasToken())
this._oVirtUserAuthenticated(this._oVirtCredentialsManager.getToken()); this._oVirtUserAuthenticated(this._oVirtCredentialsManager.getToken());
this._oVirtCredentialsManager.connect('user-authenticated', this._reset();
Lang.bind(this, this._oVirtUserAuthenticated));
}, },
begin: function(userName, hold) { _reset: function() {
this._cancellable = new Gio.Cancellable(); // Clear previous attempts to authenticate
this._hold = hold; this.verificationStatus = VerificationStatus.NOT_VERIFYING;
this._userName = null;
this._failCounter = 0;
this._updateDefaultService();
this.emit('reset');
},
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;
}
if (needsUsername) {
this.verificationStatus = VerificationStatus.ASKING_FOR_USERNAME;
this.emit('needs-username');
} else {
this._beginAuthentication();
}
},
gotUserName: function(userName) {
this._userName = userName; this._userName = userName;
this._beginAuthentication();
},
_beginAuthentication: function() {
this.verificationStatus = VerificationStatus.VERIFYING;
this._cancellable = new Gio.Cancellable();
this.reauthenticating = false; this.reauthenticating = false;
this._checkForFingerprintReader(); this._checkForFingerprintReader();
if (userName) { if (this._userName) {
// If possible, reauthenticate an already running session, // If possible, reauthenticate an already running session,
// so any session specific credentials get updated appropriately // 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)); Lang.bind(this, this._reauthenticationChannelOpened));
} else { } else {
this._client.get_user_verifier(this._cancellable, Lang.bind(this, this._userVerifierGot)); this._client.get_user_verifier(this._cancellable, Lang.bind(this, this._userVerifierGot));
} }
}, },
cancel: function() {
if (this._cancellable)
this._cancellable.cancel();
if (this._userVerifier) {
this._userVerifier.call_cancel_sync(null);
this.clear();
}
},
clear: function() { clear: function() {
if (this._cancellable) { if (this._cancellable) {
this._cancellable.cancel(); this._cancellable.cancel();
@ -198,6 +229,7 @@ const ShellUserVerifier = new Lang.Class({
} }
if (this._userVerifier) { if (this._userVerifier) {
this._userVerifier.call_cancel_sync(null);
this._userVerifier.run_dispose(); this._userVerifier.run_dispose();
this._userVerifier = null; this._userVerifier = null;
} }
@ -205,24 +237,29 @@ const ShellUserVerifier = new Lang.Class({
this._clearMessageQueue(); this._clearMessageQueue();
}, },
answerQuery: function(serviceName, answer) { _doAfterPendingMessages: function(func) {
if (!this.hasPendingMessages) { if (this.hasPendingMessages) {
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null); let signalId = this.connect('no-more-messages', Lang.bind(this, function() {
this.disconnect(signalId);
func();
}));
} else { } else {
let signalId = this.connect('no-more-messages', func();
Lang.bind(this, function() {
this.disconnect(signalId);
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
}));
} }
}, },
answerQuery: function(serviceName, answer) {
this._doAfterPendingMessages(Lang.bind(this, function() {
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
}));
},
_getIntervalForMessage: function(message) { _getIntervalForMessage: function(message) {
// We probably could be smarter here // We probably could be smarter here
return message.length * USER_READ_TIME; return message.length * USER_READ_TIME;
}, },
finishMessageQueue: function() { _finishMessageQueue: function() {
if (!this.hasPendingMessages) if (!this.hasPendingMessages)
return; return;
@ -234,7 +271,7 @@ const ShellUserVerifier = new Lang.Class({
_queueMessageTimeout: function() { _queueMessageTimeout: function() {
if (this._messageQueue.length == 0) { if (this._messageQueue.length == 0) {
this.finishMessageQueue(); this._finishMessageQueue();
return; return;
} }
@ -263,7 +300,7 @@ const ShellUserVerifier = new Lang.Class({
}, },
_clearMessageQueue: function() { _clearMessageQueue: function() {
this.finishMessageQueue(); this._finishMessageQueue();
if (this._messageQueueTimeoutId != 0) { if (this._messageQueueTimeoutId != 0) {
GLib.source_remove(this._messageQueueTimeoutId); GLib.source_remove(this._messageQueueTimeoutId);
@ -288,9 +325,9 @@ const ShellUserVerifier = new Lang.Class({
})); }));
}, },
_oVirtUserAuthenticated: function(token) { _oVirtUserAuthenticated: function() {
this._preemptingService = OVIRT_SERVICE_NAME; if (this.verificationStatus != GdmUtil.VerificationStatus.VERIFICATION_SUCCEEDED)
this.emit('ovirt-user-authenticated'); this._reset();
}, },
_checkForSmartcard: function() { _checkForSmartcard: function() {
@ -303,21 +340,29 @@ const ShellUserVerifier = new Lang.Class({
else else
smartcardDetected = this._smartcardManager.hasInsertedTokens(); smartcardDetected = this._smartcardManager.hasInsertedTokens();
if (smartcardDetected != this.smartcardDetected) { if (this._smartcardDetected == smartcardDetected)
this.smartcardDetected = smartcardDetected; return;
if (this.smartcardDetected) this._smartcardDetected = smartcardDetected;
this._preemptingService = SMARTCARD_SERVICE_NAME;
else if (this._preemptingService == SMARTCARD_SERVICE_NAME)
this._preemptingService = null;
this.emit('smartcard-status-changed'); // Most of the time we want to reset if the user inserts or removes
} // a smartcard. Smartcard insertion "preempts" what the user was
// doing, and smartcard removal aborts the preemption.
// The exceptions are: 1) Don't reset on smartcard insertion if we're already verifying
// with a smartcard
// 2) Don't reset if we've already succeeded at verification and
// the user is getting logged in.
if (this._serviceIsDefault(SMARTCARD_SERVICE_NAME) &&
this.verificationStatus == VerificationStatus.VERIFYING &&
this._smartcardDetected)
return;
if (this.verificationStatus != VerificationStatus.VERIFICATION_SUCCEEDED)
this._reset();
}, },
_reportInitError: function(where, error) { _reportInitError: function(where, error) {
logError(error, where); logError(error, where);
this._hold.release();
this._queueMessage(_("Authentication error"), MessageType.ERROR); this._queueMessage(_("Authentication error"), MessageType.ERROR);
this._verificationFailed(false); this._verificationFailed(false);
@ -343,7 +388,6 @@ const ShellUserVerifier = new Lang.Class({
this.reauthenticating = true; this.reauthenticating = true;
this._connectSignals(); this._connectSignals();
this._beginVerification(); this._beginVerification();
this._hold.release();
}, },
_userVerifierGot: function(client, result) { _userVerifierGot: function(client, result) {
@ -358,7 +402,6 @@ const ShellUserVerifier = new Lang.Class({
this._connectSignals(); this._connectSignals();
this._beginVerification(); this._beginVerification();
this._hold.release();
}, },
_connectSignals: function() { _connectSignals: function() {
@ -372,31 +415,32 @@ const ShellUserVerifier = new Lang.Class({
}, },
_getForegroundService: function() { _getForegroundService: function() {
if (this._preemptingService) if (this._oVirtCredentialsManager.hasToken())
return this._preemptingService; return OVIRT_SERVICE_NAME;
if (this._smartcardDetected)
return SMARTCARD_SERVICE_NAME;
return this._defaultService; return this._defaultService;
}, },
serviceIsForeground: function(serviceName) { _serviceIsForeground: function(serviceName) {
return serviceName == this._getForegroundService(); return serviceName == this._getForegroundService();
}, },
serviceIsDefault: function(serviceName) { _serviceIsDefault: function(serviceName) {
return serviceName == this._defaultService; return serviceName == this._defaultService;
}, },
_updateDefaultService: function() { _updateDefaultService: function() {
if (this._settings.get_boolean(PASSWORD_AUTHENTICATION_KEY)) if (this._settings.get_boolean(PASSWORD_AUTHENTICATION_KEY))
this._defaultService = PASSWORD_SERVICE_NAME; this._defaultService = PASSWORD_SERVICE_NAME;
else if (this.smartcardDetected) else if (this._smartcardDetected)
this._defaultService = SMARTCARD_SERVICE_NAME; this._defaultService = SMARTCARD_SERVICE_NAME;
else if (this._haveFingerprintReader) else if (this._haveFingerprintReader)
this._defaultService = FINGERPRINT_SERVICE_NAME; this._defaultService = FINGERPRINT_SERVICE_NAME;
}, },
_startService: function(serviceName) { _startService: function(serviceName) {
this._hold.acquire();
if (this._userName) { if (this._userName) {
this._userVerifier.call_begin_verification_for_user(serviceName, this._userVerifier.call_begin_verification_for_user(serviceName,
this._userName, this._userName,
@ -410,8 +454,6 @@ const ShellUserVerifier = new Lang.Class({
this._reportInitError('Failed to start verification for user', e); this._reportInitError('Failed to start verification for user', e);
return; return;
} }
this._hold.release();
})); }));
} else { } else {
this._userVerifier.call_begin_verification(serviceName, this._userVerifier.call_begin_verification(serviceName,
@ -425,8 +467,6 @@ const ShellUserVerifier = new Lang.Class({
this._reportInitError('Failed to start verification', e); this._reportInitError('Failed to start verification', e);
return; return;
} }
this._hold.release();
})); }));
} }
}, },
@ -434,12 +474,12 @@ const ShellUserVerifier = new Lang.Class({
_beginVerification: function() { _beginVerification: function() {
this._startService(this._getForegroundService()); this._startService(this._getForegroundService());
if (this._userName && this._haveFingerprintReader && !this.serviceIsForeground(FINGERPRINT_SERVICE_NAME)) if (this._userName && this._haveFingerprintReader && !this._serviceIsForeground(FINGERPRINT_SERVICE_NAME))
this._startService(FINGERPRINT_SERVICE_NAME); this._startService(FINGERPRINT_SERVICE_NAME);
}, },
_onInfo: function(client, serviceName, info) { _onInfo: function(client, serviceName, info) {
if (this.serviceIsForeground(serviceName)) { if (this._serviceIsForeground(serviceName)) {
this._queueMessage(info, MessageType.INFO); this._queueMessage(info, MessageType.INFO);
} else if (serviceName == FINGERPRINT_SERVICE_NAME && } else if (serviceName == FINGERPRINT_SERVICE_NAME &&
this._haveFingerprintReader) { this._haveFingerprintReader) {
@ -454,21 +494,21 @@ const ShellUserVerifier = new Lang.Class({
}, },
_onProblem: function(client, serviceName, problem) { _onProblem: function(client, serviceName, problem) {
if (!this.serviceIsForeground(serviceName)) if (!this._serviceIsForeground(serviceName))
return; return;
this._queueMessage(problem, MessageType.ERROR); this._queueMessage(problem, MessageType.ERROR);
}, },
_onInfoQuery: function(client, serviceName, question) { _onInfoQuery: function(client, serviceName, question) {
if (!this.serviceIsForeground(serviceName)) if (!this._serviceIsForeground(serviceName))
return; return;
this.emit('ask-question', serviceName, question, ''); this.emit('ask-question', serviceName, question, '');
}, },
_onSecretInfoQuery: function(client, serviceName, secretQuestion) { _onSecretInfoQuery: function(client, serviceName, secretQuestion) {
if (!this.serviceIsForeground(serviceName)) if (!this._serviceIsForeground(serviceName))
return; return;
if (serviceName == OVIRT_SERVICE_NAME) { if (serviceName == OVIRT_SERVICE_NAME) {
@ -481,24 +521,11 @@ const ShellUserVerifier = new Lang.Class({
}, },
_onReset: function() { _onReset: function() {
// Clear previous attempts to authenticate this._reset();
this._failCounter = 0;
this._updateDefaultService();
this.emit('reset');
}, },
_onVerificationComplete: function() { _onVerificationComplete: function() {
this.emit('verification-complete'); this.verificationStatus = VerificationStatus.VERIFICATION_SUCCEEDED;
},
_cancelAndReset: function() {
this.cancel();
this._onReset();
},
_retry: function() {
this.begin(this._userName, new Batch.Hold());
}, },
_verificationFailed: function(retry) { _verificationFailed: function(retry) {
@ -511,38 +538,23 @@ const ShellUserVerifier = new Lang.Class({
let canRetry = retry && this._userName && let canRetry = retry && this._userName &&
this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY); this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY);
if (canRetry) { this.verificationStatus = VerificationStatus.VERIFICATION_FAILED;
if (!this.hasPendingMessages) {
this._retry();
} else {
let signalId = this.connect('no-more-messages',
Lang.bind(this, function() {
this.disconnect(signalId);
this._retry();
}));
}
} else {
if (!this.hasPendingMessages) {
this._cancelAndReset();
} else {
let signalId = this.connect('no-more-messages',
Lang.bind(this, function() {
this.disconnect(signalId);
this._cancelAndReset();
}));
}
}
this.emit('verification-failed'); this.emit('verification-failed');
this._doAfterPendingMessages(Lang.bind(this, function() {
if (canRetry)
this._beginAuthentication();
else
this.clear();
}));
}, },
_onConversationStopped: function(client, serviceName) { _onConversationStopped: function(client, serviceName) {
// If the login failed with the preauthenticated oVirt credentials // If the login failed with the preauthenticated oVirt credentials
// then discard the credentials and revert to default authentication // then discard the credentials and revert to default authentication
// mechanism. // mechanism.
if (this.serviceIsForeground(OVIRT_SERVICE_NAME)) { if (this._serviceIsForeground(OVIRT_SERVICE_NAME)) {
this._oVirtCredentialsManager.resetToken(); this._oVirtCredentialsManager.resetToken();
this._preemptingService = null;
this._verificationFailed(false); this._verificationFailed(false);
return; return;
} }
@ -550,7 +562,7 @@ const ShellUserVerifier = new Lang.Class({
// if the password service fails, then cancel everything. // if the password service fails, then cancel everything.
// But if, e.g., fingerprint fails, still give // But if, e.g., fingerprint fails, still give
// password authentication a chance to succeed // password authentication a chance to succeed
if (this.serviceIsForeground(serviceName)) { if (this._serviceIsForeground(serviceName)) {
this._verificationFailed(true); this._verificationFailed(true);
} }
}, },

View File

@ -39,6 +39,7 @@ const SystemdLoginSessionIface = '<node> \
<interface name="org.freedesktop.login1.Session"> \ <interface name="org.freedesktop.login1.Session"> \
<signal name="Lock" /> \ <signal name="Lock" /> \
<signal name="Unlock" /> \ <signal name="Unlock" /> \
<property name="Active" access="readonly" /> \
</interface> \ </interface> \
</node>'; </node>';

View File

@ -203,6 +203,10 @@ function _initializeUI() {
ExtensionDownloader.init(); ExtensionDownloader.init();
ExtensionSystem.init(); ExtensionSystem.init();
layoutManager.connect('startup-prepared', function() {
Meta.activate_session();
});
if (sessionMode.isGreeter && screenShield) { if (sessionMode.isGreeter && screenShield) {
layoutManager.connect('startup-prepared', function() { layoutManager.connect('startup-prepared', function() {
screenShield.showDialog(); screenShield.showDialog();

View File

@ -53,8 +53,8 @@ const UnlockDialog = new Lang.Class({
this._authPrompt = new AuthPrompt.AuthPrompt(new Gdm.Client(), AuthPrompt.AuthPromptMode.UNLOCK_ONLY); this._authPrompt = new AuthPrompt.AuthPrompt(new Gdm.Client(), AuthPrompt.AuthPromptMode.UNLOCK_ONLY);
this._authPrompt.connect('failed', Lang.bind(this, this._fail)); this._authPrompt.connect('failed', Lang.bind(this, this._fail));
this._authPrompt.connect('cancelled', Lang.bind(this, this._fail));
this._authPrompt.connect('reset', Lang.bind(this, this._onReset)); 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.setPasswordChar('\u25cf');
this._authPrompt.nextButton.label = _("Unlock"); this._authPrompt.nextButton.label = _("Unlock");
@ -101,6 +101,10 @@ const UnlockDialog = new Lang.Class({
}, },
_onReset: function(authPrompt, beginRequest) { _onReset: function(authPrompt, beginRequest) {
this._authPrompt.begin();
},
_onNeedsUserName: function() {
let userName; let userName;
if (beginRequest == AuthPrompt.BeginRequestType.PROVIDE_USERNAME) { if (beginRequest == AuthPrompt.BeginRequestType.PROVIDE_USERNAME) {
this._authPrompt.setUser(this._user); this._authPrompt.setUser(this._user);
@ -109,7 +113,7 @@ const UnlockDialog = new Lang.Class({
userName = null; userName = null;
} }
this._authPrompt.begin({ userName: userName }); this._authPrompt.gotUserName(userName);
}, },
_escape: function() { _escape: function() {