2012-07-17 14:54:07 -04:00
|
|
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
2019-01-31 09:07:06 -05:00
|
|
|
/* exported BANNER_MESSAGE_KEY, BANNER_MESSAGE_TEXT_KEY, LOGO_KEY,
|
|
|
|
DISABLE_USER_LIST_KEY, fadeInActor, fadeOutActor, cloneAndFadeOutActor */
|
2012-07-17 14:54:07 -04:00
|
|
|
|
2019-02-08 22:21:36 -05:00
|
|
|
const { Clutter, Gio, GLib } = imports.gi;
|
2012-07-17 14:54:07 -04:00
|
|
|
const Signals = imports.signals;
|
|
|
|
|
|
|
|
const Batch = imports.gdm.batch;
|
|
|
|
const Fprint = imports.gdm.fingerprint;
|
2013-10-10 04:21:47 -04:00
|
|
|
const OVirt = imports.gdm.oVirt;
|
2012-07-17 14:54:07 -04:00
|
|
|
const Main = imports.ui.main;
|
2012-08-03 11:10:45 -04:00
|
|
|
const Params = imports.misc.params;
|
2013-06-27 08:54:19 -04:00
|
|
|
const SmartcardManager = imports.misc.smartcardManager;
|
2012-07-17 14:54:07 -04:00
|
|
|
|
2017-08-10 12:35:07 -04:00
|
|
|
var PASSWORD_SERVICE_NAME = 'gdm-password';
|
|
|
|
var FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
|
|
|
|
var SMARTCARD_SERVICE_NAME = 'gdm-smartcard';
|
|
|
|
var OVIRT_SERVICE_NAME = 'gdm-ovirtcred';
|
2019-08-01 19:13:10 -04:00
|
|
|
var FADE_ANIMATION_TIME = 160;
|
|
|
|
var CLONE_FADE_ANIMATION_TIME = 250;
|
2012-07-17 14:54:07 -04:00
|
|
|
|
2017-08-10 12:35:07 -04:00
|
|
|
var LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
|
|
|
|
var PASSWORD_AUTHENTICATION_KEY = 'enable-password-authentication';
|
|
|
|
var FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
|
|
|
|
var SMARTCARD_AUTHENTICATION_KEY = 'enable-smartcard-authentication';
|
|
|
|
var BANNER_MESSAGE_KEY = 'banner-message-enable';
|
|
|
|
var BANNER_MESSAGE_TEXT_KEY = 'banner-message-text';
|
|
|
|
var ALLOWED_FAILURES_KEY = 'allowed-failures';
|
2012-07-17 14:54:07 -04:00
|
|
|
|
2017-08-10 12:35:07 -04:00
|
|
|
var LOGO_KEY = 'logo';
|
|
|
|
var DISABLE_USER_LIST_KEY = 'disable-user-list';
|
2012-07-17 14:54:07 -04:00
|
|
|
|
2015-03-27 09:36:05 -04:00
|
|
|
// Give user 48ms to read each character of a PAM message
|
2019-01-28 20:18:52 -05:00
|
|
|
var USER_READ_TIME = 48;
|
2013-03-18 00:59:56 -04:00
|
|
|
|
2017-07-18 13:47:27 -04:00
|
|
|
var MessageType = {
|
2013-08-19 12:00:33 -04:00
|
|
|
NONE: 0,
|
|
|
|
ERROR: 1,
|
|
|
|
INFO: 2,
|
|
|
|
HINT: 3
|
|
|
|
};
|
|
|
|
|
2012-07-17 14:54:07 -04:00
|
|
|
function fadeInActor(actor) {
|
|
|
|
if (actor.opacity == 255 && actor.visible)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
let hold = new Batch.Hold();
|
|
|
|
actor.show();
|
2019-02-01 08:41:55 -05:00
|
|
|
let [, naturalHeight] = actor.get_preferred_height(-1);
|
2012-07-17 14:54:07 -04:00
|
|
|
|
|
|
|
actor.opacity = 0;
|
|
|
|
actor.set_height(0);
|
2018-07-20 15:46:19 -04:00
|
|
|
actor.ease({
|
|
|
|
opacity: 255,
|
|
|
|
height: naturalHeight,
|
|
|
|
duration: FADE_ANIMATION_TIME,
|
|
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
|
|
onComplete: () => {
|
|
|
|
this.set_height(-1);
|
|
|
|
hold.release();
|
|
|
|
}
|
|
|
|
});
|
2012-07-17 14:54:07 -04:00
|
|
|
|
|
|
|
return hold;
|
|
|
|
}
|
|
|
|
|
|
|
|
function fadeOutActor(actor) {
|
|
|
|
if (!actor.visible || actor.opacity == 0) {
|
|
|
|
actor.opacity = 0;
|
|
|
|
actor.hide();
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
let hold = new Batch.Hold();
|
2018-07-20 15:46:19 -04:00
|
|
|
actor.ease({
|
|
|
|
opacity: 0,
|
|
|
|
height: 0,
|
|
|
|
duration: FADE_ANIMATION_TIME,
|
|
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
|
|
onComplete: () => {
|
|
|
|
this.hide();
|
|
|
|
this.set_height(-1);
|
|
|
|
hold.release();
|
|
|
|
}
|
|
|
|
});
|
2012-07-17 14:54:07 -04:00
|
|
|
return hold;
|
|
|
|
}
|
|
|
|
|
2013-02-06 14:18:26 -05:00
|
|
|
function cloneAndFadeOutActor(actor) {
|
|
|
|
// Immediately hide actor so its sibling can have its space
|
|
|
|
// and position, but leave a non-reactive clone on-screen,
|
|
|
|
// so from the user's point of view it smoothly fades away
|
|
|
|
// and reveals its sibling.
|
|
|
|
actor.hide();
|
|
|
|
|
|
|
|
let clone = new Clutter.Clone({ source: actor,
|
|
|
|
reactive: false });
|
|
|
|
|
|
|
|
Main.uiGroup.add_child(clone);
|
|
|
|
|
|
|
|
let [x, y] = actor.get_transformed_position();
|
|
|
|
clone.set_position(x, y);
|
|
|
|
|
|
|
|
let hold = new Batch.Hold();
|
2018-07-20 15:46:19 -04:00
|
|
|
clone.ease({
|
|
|
|
opacity: 0,
|
|
|
|
duration: CLONE_FADE_ANIMATION_TIME,
|
|
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
|
|
onComplete: () => {
|
|
|
|
clone.destroy();
|
|
|
|
hold.release();
|
|
|
|
}
|
|
|
|
});
|
2013-02-06 14:18:26 -05:00
|
|
|
return hold;
|
|
|
|
}
|
|
|
|
|
2017-10-30 21:19:44 -04:00
|
|
|
var ShellUserVerifier = class {
|
|
|
|
constructor(client, params) {
|
2012-08-03 11:10:45 -04:00
|
|
|
params = Params.parse(params, { reauthenticationOnly: false });
|
|
|
|
this._reauthOnly = params.reauthenticationOnly;
|
|
|
|
|
2012-07-17 14:54:07 -04:00
|
|
|
this._client = client;
|
|
|
|
|
2017-06-12 22:24:12 -04:00
|
|
|
this._defaultService = null;
|
|
|
|
this._preemptingService = null;
|
|
|
|
|
2014-06-24 15:17:09 -04:00
|
|
|
this._settings = new Gio.Settings({ schema_id: LOGIN_SCREEN_SCHEMA });
|
2013-07-29 14:18:30 -04:00
|
|
|
this._settings.connect('changed',
|
2017-12-01 19:27:35 -05:00
|
|
|
this._updateDefaultService.bind(this));
|
util: abstract out default auth service in code
Right now, the primary way a user logs in is with
a password. They can also swipe their finger, if their
fingerprint is enrolled, but it's expected the fingerprint
auth service won't ask questions the user has to respond to
by typing. As such, we ignore questions that comes from
anything but the main auth service: gdm-password.
In the future, if a user inserts a smartcard, we'll want
to treat the gdm-smartcard service as the main auth service,
and let any questions from it get to the user.
This commit tries to prepare for that eventuality by storing
the name of the default auth service away in a _defaultService variable
before verification has begun, and then later checking incoming
queries against that service instead of checking against
string 'gdm-password' directly.
Of course, right now, _defaultService is always gdm-password.
https://bugzilla.gnome.org/show_bug.cgi?id=683437
2013-07-28 19:42:26 -04:00
|
|
|
this._updateDefaultService();
|
2012-07-17 14:54:07 -04:00
|
|
|
|
2017-03-14 16:02:51 -04:00
|
|
|
this._fprintManager = Fprint.FprintManager();
|
2013-06-27 08:54:19 -04:00
|
|
|
this._smartcardManager = SmartcardManager.getSmartcardManager();
|
|
|
|
|
|
|
|
// We check for smartcards right away, since an inserted smartcard
|
|
|
|
// at startup should result in immediately initiating authentication.
|
2017-06-26 14:47:19 -04:00
|
|
|
// This is different than fingerprint readers, where we only check them
|
2013-06-27 08:54:19 -04:00
|
|
|
// after a user has been picked.
|
2017-06-12 22:24:12 -04:00
|
|
|
this.smartcardDetected = false;
|
2013-06-27 08:54:19 -04:00
|
|
|
this._checkForSmartcard();
|
|
|
|
|
2014-10-09 14:10:12 -04:00
|
|
|
this._smartcardInsertedId = this._smartcardManager.connect('smartcard-inserted',
|
2017-12-01 19:27:35 -05:00
|
|
|
this._checkForSmartcard.bind(this));
|
2014-10-09 14:10:12 -04:00
|
|
|
this._smartcardRemovedId = this._smartcardManager.connect('smartcard-removed',
|
2017-12-01 19:27:35 -05:00
|
|
|
this._checkForSmartcard.bind(this));
|
2013-06-27 08:54:19 -04:00
|
|
|
|
2013-03-18 00:59:56 -04:00
|
|
|
this._messageQueue = [];
|
|
|
|
this._messageQueueTimeoutId = 0;
|
|
|
|
this.hasPendingMessages = false;
|
2013-07-22 10:59:57 -04:00
|
|
|
this.reauthenticating = false;
|
2012-08-07 10:49:22 -04:00
|
|
|
|
|
|
|
this._failCounter = 0;
|
2013-10-10 04:21:47 -04:00
|
|
|
|
|
|
|
this._oVirtCredentialsManager = OVirt.getOVirtCredentialsManager();
|
|
|
|
|
|
|
|
if (this._oVirtCredentialsManager.hasToken())
|
|
|
|
this._oVirtUserAuthenticated(this._oVirtCredentialsManager.getToken());
|
|
|
|
|
2014-10-09 14:10:12 -04:00
|
|
|
this._oVirtUserAuthenticatedId = this._oVirtCredentialsManager.connect('user-authenticated',
|
2017-12-01 19:27:35 -05:00
|
|
|
this._oVirtUserAuthenticated.bind(this));
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2012-07-17 14:54:07 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
begin(userName, hold) {
|
2012-08-26 08:54:02 -04:00
|
|
|
this._cancellable = new Gio.Cancellable();
|
2012-07-17 14:54:07 -04:00
|
|
|
this._hold = hold;
|
|
|
|
this._userName = userName;
|
2013-07-22 10:59:57 -04:00
|
|
|
this.reauthenticating = false;
|
2012-07-17 14:54:07 -04:00
|
|
|
|
2012-08-26 08:54:02 -04:00
|
|
|
this._checkForFingerprintReader();
|
|
|
|
|
2012-07-17 14:54:07 -04:00
|
|
|
if (userName) {
|
|
|
|
// If possible, reauthenticate an already running session,
|
|
|
|
// so any session specific credentials get updated appropriately
|
|
|
|
this._client.open_reauthentication_channel(userName, this._cancellable,
|
2017-12-01 19:27:35 -05:00
|
|
|
this._reauthenticationChannelOpened.bind(this));
|
2012-07-17 14:54:07 -04:00
|
|
|
} else {
|
2017-12-01 19:27:35 -05:00
|
|
|
this._client.get_user_verifier(this._cancellable, this._userVerifierGot.bind(this));
|
2012-07-17 14:54:07 -04:00
|
|
|
}
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2012-07-17 14:54:07 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
cancel() {
|
2012-08-26 08:54:02 -04:00
|
|
|
if (this._cancellable)
|
|
|
|
this._cancellable.cancel();
|
2012-07-17 14:54:07 -04:00
|
|
|
|
2013-07-29 10:52:02 -04:00
|
|
|
if (this._userVerifier) {
|
2012-07-17 14:54:07 -04:00
|
|
|
this._userVerifier.call_cancel_sync(null);
|
2013-07-29 10:52:02 -04:00
|
|
|
this.clear();
|
|
|
|
}
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2012-07-17 14:54:07 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_clearUserVerifier() {
|
2014-10-09 14:10:12 -04:00
|
|
|
if (this._userVerifier) {
|
|
|
|
this._userVerifier.run_dispose();
|
|
|
|
this._userVerifier = null;
|
|
|
|
}
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2014-10-09 14:10:12 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
clear() {
|
2012-08-26 08:54:02 -04:00
|
|
|
if (this._cancellable) {
|
|
|
|
this._cancellable.cancel();
|
|
|
|
this._cancellable = null;
|
|
|
|
}
|
2012-07-17 14:54:07 -04:00
|
|
|
|
2014-10-09 14:10:12 -04:00
|
|
|
this._clearUserVerifier();
|
2013-03-18 00:59:56 -04:00
|
|
|
this._clearMessageQueue();
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2012-07-17 14:54:07 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
destroy() {
|
2014-10-09 14:10:12 -04:00
|
|
|
this.clear();
|
|
|
|
|
|
|
|
this._settings.run_dispose();
|
|
|
|
this._settings = null;
|
|
|
|
|
|
|
|
this._smartcardManager.disconnect(this._smartcardInsertedId);
|
|
|
|
this._smartcardManager.disconnect(this._smartcardRemovedId);
|
|
|
|
this._smartcardManager = null;
|
|
|
|
|
|
|
|
this._oVirtCredentialsManager.disconnect(this._oVirtUserAuthenticatedId);
|
|
|
|
this._oVirtCredentialsManager = null;
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2014-10-09 14:10:12 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
answerQuery(serviceName, answer) {
|
2013-07-16 15:48:27 -04:00
|
|
|
if (!this.hasPendingMessages) {
|
2013-03-18 00:59:56 -04:00
|
|
|
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
|
|
|
|
} else {
|
2017-10-30 20:38:18 -04:00
|
|
|
let signalId = this.connect('no-more-messages', () => {
|
|
|
|
this.disconnect(signalId);
|
|
|
|
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
|
|
|
|
});
|
2013-03-18 00:59:56 -04:00
|
|
|
}
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2013-03-18 00:59:56 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_getIntervalForMessage(message) {
|
2013-03-18 00:59:56 -04:00
|
|
|
// We probably could be smarter here
|
|
|
|
return message.length * USER_READ_TIME;
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2013-03-18 00:59:56 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
finishMessageQueue() {
|
2013-03-18 00:59:56 -04:00
|
|
|
if (!this.hasPendingMessages)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this._messageQueue = [];
|
2012-10-29 12:39:00 -04:00
|
|
|
|
2013-03-18 00:59:56 -04:00
|
|
|
this.hasPendingMessages = false;
|
|
|
|
this.emit('no-more-messages');
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2013-03-18 00:59:56 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_queueMessageTimeout() {
|
2013-03-18 00:59:56 -04:00
|
|
|
if (this._messageQueue.length == 0) {
|
|
|
|
this.finishMessageQueue();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this._messageQueueTimeoutId != 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let message = this._messageQueue.shift();
|
2013-08-19 12:00:33 -04:00
|
|
|
|
|
|
|
this.emit('show-message', message.text, message.type);
|
2013-03-18 00:59:56 -04:00
|
|
|
|
|
|
|
this._messageQueueTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
|
|
|
message.interval,
|
2017-10-30 20:38:18 -04:00
|
|
|
() => {
|
2013-03-18 00:59:56 -04:00
|
|
|
this._messageQueueTimeoutId = 0;
|
|
|
|
this._queueMessageTimeout();
|
2013-11-28 19:45:39 -05:00
|
|
|
return GLib.SOURCE_REMOVE;
|
2017-10-30 20:38:18 -04:00
|
|
|
});
|
2014-04-10 13:26:52 -04:00
|
|
|
GLib.Source.set_name_by_id(this._messageQueueTimeoutId, '[gnome-shell] this._queueMessageTimeout');
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2013-03-18 00:59:56 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_queueMessage(message, messageType) {
|
2013-03-18 00:59:56 -04:00
|
|
|
let interval = this._getIntervalForMessage(message);
|
|
|
|
|
|
|
|
this.hasPendingMessages = true;
|
2013-08-19 12:00:33 -04:00
|
|
|
this._messageQueue.push({ text: message, type: messageType, interval: interval });
|
2013-03-18 00:59:56 -04:00
|
|
|
this._queueMessageTimeout();
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2013-03-18 00:59:56 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_clearMessageQueue() {
|
2013-03-18 00:59:56 -04:00
|
|
|
this.finishMessageQueue();
|
|
|
|
|
|
|
|
if (this._messageQueueTimeoutId != 0) {
|
|
|
|
GLib.source_remove(this._messageQueueTimeoutId);
|
|
|
|
this._messageQueueTimeoutId = 0;
|
|
|
|
}
|
2013-08-19 12:00:33 -04:00
|
|
|
this.emit('show-message', null, MessageType.NONE);
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2012-07-17 14:54:07 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_checkForFingerprintReader() {
|
2012-07-17 14:54:07 -04:00
|
|
|
this._haveFingerprintReader = false;
|
|
|
|
|
2017-03-14 16:02:51 -04:00
|
|
|
if (!this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY) ||
|
|
|
|
this._fprintManager == null) {
|
2013-07-29 14:18:30 -04:00
|
|
|
this._updateDefaultService();
|
2012-07-17 14:54:07 -04:00
|
|
|
return;
|
2013-07-29 14:18:30 -04:00
|
|
|
}
|
2012-07-17 14:54:07 -04:00
|
|
|
|
2017-10-30 20:38:18 -04:00
|
|
|
this._fprintManager.GetDefaultDeviceRemote(Gio.DBusCallFlags.NONE, this._cancellable,
|
|
|
|
(device, error) => {
|
2014-10-09 14:25:48 -04:00
|
|
|
if (!error && device) {
|
2012-07-17 14:54:07 -04:00
|
|
|
this._haveFingerprintReader = true;
|
2013-07-29 14:18:30 -04:00
|
|
|
this._updateDefaultService();
|
2014-10-09 14:25:48 -04:00
|
|
|
}
|
2017-10-30 20:38:18 -04:00
|
|
|
});
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2012-07-17 14:54:07 -04:00
|
|
|
|
2019-01-31 09:08:10 -05:00
|
|
|
_oVirtUserAuthenticated(_token) {
|
2013-10-10 04:21:47 -04:00
|
|
|
this._preemptingService = OVIRT_SERVICE_NAME;
|
|
|
|
this.emit('ovirt-user-authenticated');
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2013-10-10 04:21:47 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_checkForSmartcard() {
|
2013-06-27 08:54:19 -04:00
|
|
|
let smartcardDetected;
|
|
|
|
|
|
|
|
if (!this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY))
|
|
|
|
smartcardDetected = false;
|
2014-03-13 13:47:50 -04:00
|
|
|
else if (this._reauthOnly)
|
2013-06-27 08:54:19 -04:00
|
|
|
smartcardDetected = this._smartcardManager.hasInsertedLoginToken();
|
|
|
|
else
|
|
|
|
smartcardDetected = this._smartcardManager.hasInsertedTokens();
|
|
|
|
|
|
|
|
if (smartcardDetected != this.smartcardDetected) {
|
|
|
|
this.smartcardDetected = smartcardDetected;
|
|
|
|
|
|
|
|
if (this.smartcardDetected)
|
|
|
|
this._preemptingService = SMARTCARD_SERVICE_NAME;
|
|
|
|
else if (this._preemptingService == SMARTCARD_SERVICE_NAME)
|
|
|
|
this._preemptingService = null;
|
|
|
|
|
|
|
|
this.emit('smartcard-status-changed');
|
|
|
|
}
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2013-06-27 08:54:19 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_reportInitError(where, error) {
|
2012-09-06 10:40:13 -04:00
|
|
|
logError(error, where);
|
2012-10-03 15:25:49 -04:00
|
|
|
this._hold.release();
|
2012-09-06 10:40:13 -04:00
|
|
|
|
2013-08-19 12:00:33 -04:00
|
|
|
this._queueMessage(_("Authentication error"), MessageType.ERROR);
|
2012-09-06 10:40:13 -04:00
|
|
|
this._verificationFailed(false);
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2012-09-06 10:40:13 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_reauthenticationChannelOpened(client, result) {
|
2012-07-17 14:54:07 -04:00
|
|
|
try {
|
2014-10-09 14:10:12 -04:00
|
|
|
this._clearUserVerifier();
|
2012-07-17 14:54:07 -04:00
|
|
|
this._userVerifier = client.open_reauthentication_channel_finish(result);
|
2019-01-28 20:26:39 -05:00
|
|
|
} catch (e) {
|
2018-07-14 21:17:42 -04:00
|
|
|
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
|
|
|
|
return;
|
|
|
|
if (e.matches(Gio.DBusError, Gio.DBusError.ACCESS_DENIED) &&
|
|
|
|
!this._reauthOnly) {
|
|
|
|
// Gdm emits org.freedesktop.DBus.Error.AccessDenied when there
|
|
|
|
// is no session to reauthenticate. Fall back to performing
|
|
|
|
// verification from this login session
|
|
|
|
client.get_user_verifier(this._cancellable,
|
|
|
|
this._userVerifierGot.bind(this));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-09-06 10:40:13 -04:00
|
|
|
this._reportInitError('Failed to open reauthentication channel', e);
|
|
|
|
return;
|
2012-07-17 14:54:07 -04:00
|
|
|
}
|
2012-09-06 10:40:13 -04:00
|
|
|
|
2013-07-22 10:59:57 -04:00
|
|
|
this.reauthenticating = true;
|
2012-09-06 10:40:13 -04:00
|
|
|
this._connectSignals();
|
|
|
|
this._beginVerification();
|
|
|
|
this._hold.release();
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2012-07-17 14:54:07 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_userVerifierGot(client, result) {
|
2012-08-26 08:54:02 -04:00
|
|
|
try {
|
2014-10-09 14:10:12 -04:00
|
|
|
this._clearUserVerifier();
|
2012-08-26 08:54:02 -04:00
|
|
|
this._userVerifier = client.get_user_verifier_finish(result);
|
2019-01-28 20:26:39 -05:00
|
|
|
} catch (e) {
|
2018-07-14 21:17:42 -04:00
|
|
|
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
|
|
|
|
return;
|
2012-09-06 10:40:13 -04:00
|
|
|
this._reportInitError('Failed to obtain user verifier', e);
|
2012-08-26 08:54:02 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-07-17 14:54:07 -04:00
|
|
|
this._connectSignals();
|
|
|
|
this._beginVerification();
|
|
|
|
this._hold.release();
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2012-07-17 14:54:07 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_connectSignals() {
|
2017-12-01 19:27:35 -05:00
|
|
|
this._userVerifier.connect('info', this._onInfo.bind(this));
|
|
|
|
this._userVerifier.connect('problem', this._onProblem.bind(this));
|
|
|
|
this._userVerifier.connect('info-query', this._onInfoQuery.bind(this));
|
|
|
|
this._userVerifier.connect('secret-info-query', this._onSecretInfoQuery.bind(this));
|
|
|
|
this._userVerifier.connect('conversation-stopped', this._onConversationStopped.bind(this));
|
|
|
|
this._userVerifier.connect('reset', this._onReset.bind(this));
|
|
|
|
this._userVerifier.connect('verification-complete', this._onVerificationComplete.bind(this));
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2012-07-17 14:54:07 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_getForegroundService() {
|
2013-06-27 08:54:19 -04:00
|
|
|
if (this._preemptingService)
|
|
|
|
return this._preemptingService;
|
|
|
|
|
util: abstract out default auth service in code
Right now, the primary way a user logs in is with
a password. They can also swipe their finger, if their
fingerprint is enrolled, but it's expected the fingerprint
auth service won't ask questions the user has to respond to
by typing. As such, we ignore questions that comes from
anything but the main auth service: gdm-password.
In the future, if a user inserts a smartcard, we'll want
to treat the gdm-smartcard service as the main auth service,
and let any questions from it get to the user.
This commit tries to prepare for that eventuality by storing
the name of the default auth service away in a _defaultService variable
before verification has begun, and then later checking incoming
queries against that service instead of checking against
string 'gdm-password' directly.
Of course, right now, _defaultService is always gdm-password.
https://bugzilla.gnome.org/show_bug.cgi?id=683437
2013-07-28 19:42:26 -04:00
|
|
|
return this._defaultService;
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
util: abstract out default auth service in code
Right now, the primary way a user logs in is with
a password. They can also swipe their finger, if their
fingerprint is enrolled, but it's expected the fingerprint
auth service won't ask questions the user has to respond to
by typing. As such, we ignore questions that comes from
anything but the main auth service: gdm-password.
In the future, if a user inserts a smartcard, we'll want
to treat the gdm-smartcard service as the main auth service,
and let any questions from it get to the user.
This commit tries to prepare for that eventuality by storing
the name of the default auth service away in a _defaultService variable
before verification has begun, and then later checking incoming
queries against that service instead of checking against
string 'gdm-password' directly.
Of course, right now, _defaultService is always gdm-password.
https://bugzilla.gnome.org/show_bug.cgi?id=683437
2013-07-28 19:42:26 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
serviceIsForeground(serviceName) {
|
util: abstract out default auth service in code
Right now, the primary way a user logs in is with
a password. They can also swipe their finger, if their
fingerprint is enrolled, but it's expected the fingerprint
auth service won't ask questions the user has to respond to
by typing. As such, we ignore questions that comes from
anything but the main auth service: gdm-password.
In the future, if a user inserts a smartcard, we'll want
to treat the gdm-smartcard service as the main auth service,
and let any questions from it get to the user.
This commit tries to prepare for that eventuality by storing
the name of the default auth service away in a _defaultService variable
before verification has begun, and then later checking incoming
queries against that service instead of checking against
string 'gdm-password' directly.
Of course, right now, _defaultService is always gdm-password.
https://bugzilla.gnome.org/show_bug.cgi?id=683437
2013-07-28 19:42:26 -04:00
|
|
|
return serviceName == this._getForegroundService();
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
util: abstract out default auth service in code
Right now, the primary way a user logs in is with
a password. They can also swipe their finger, if their
fingerprint is enrolled, but it's expected the fingerprint
auth service won't ask questions the user has to respond to
by typing. As such, we ignore questions that comes from
anything but the main auth service: gdm-password.
In the future, if a user inserts a smartcard, we'll want
to treat the gdm-smartcard service as the main auth service,
and let any questions from it get to the user.
This commit tries to prepare for that eventuality by storing
the name of the default auth service away in a _defaultService variable
before verification has begun, and then later checking incoming
queries against that service instead of checking against
string 'gdm-password' directly.
Of course, right now, _defaultService is always gdm-password.
https://bugzilla.gnome.org/show_bug.cgi?id=683437
2013-07-28 19:42:26 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
serviceIsDefault(serviceName) {
|
2013-06-27 08:54:19 -04:00
|
|
|
return serviceName == this._defaultService;
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2013-06-27 08:54:19 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_updateDefaultService() {
|
2013-07-29 14:18:30 -04:00
|
|
|
if (this._settings.get_boolean(PASSWORD_AUTHENTICATION_KEY))
|
|
|
|
this._defaultService = PASSWORD_SERVICE_NAME;
|
2014-11-14 15:57:16 -05:00
|
|
|
else if (this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY))
|
2013-06-27 08:54:19 -04:00
|
|
|
this._defaultService = SMARTCARD_SERVICE_NAME;
|
2013-07-29 14:18:30 -04:00
|
|
|
else if (this._haveFingerprintReader)
|
|
|
|
this._defaultService = FINGERPRINT_SERVICE_NAME;
|
2015-07-01 11:18:44 -04:00
|
|
|
|
|
|
|
if (!this._defaultService) {
|
|
|
|
log("no authentication service is enabled, using password authentication");
|
|
|
|
this._defaultService = PASSWORD_SERVICE_NAME;
|
|
|
|
}
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
util: abstract out default auth service in code
Right now, the primary way a user logs in is with
a password. They can also swipe their finger, if their
fingerprint is enrolled, but it's expected the fingerprint
auth service won't ask questions the user has to respond to
by typing. As such, we ignore questions that comes from
anything but the main auth service: gdm-password.
In the future, if a user inserts a smartcard, we'll want
to treat the gdm-smartcard service as the main auth service,
and let any questions from it get to the user.
This commit tries to prepare for that eventuality by storing
the name of the default auth service away in a _defaultService variable
before verification has begun, and then later checking incoming
queries against that service instead of checking against
string 'gdm-password' directly.
Of course, right now, _defaultService is always gdm-password.
https://bugzilla.gnome.org/show_bug.cgi?id=683437
2013-07-28 19:42:26 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_startService(serviceName) {
|
2012-07-17 14:54:07 -04:00
|
|
|
this._hold.acquire();
|
2013-08-21 18:05:55 -04:00
|
|
|
if (this._userName) {
|
2019-01-29 14:36:54 -05:00
|
|
|
this._userVerifier.call_begin_verification_for_user(serviceName, this._userName, this._cancellable, (obj, result) => {
|
|
|
|
try {
|
|
|
|
obj.call_begin_verification_for_user_finish(result);
|
|
|
|
} catch (e) {
|
|
|
|
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
|
|
|
|
return;
|
|
|
|
this._reportInitError('Failed to start verification for user', e);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this._hold.release();
|
|
|
|
});
|
2013-08-21 18:05:55 -04:00
|
|
|
} else {
|
2019-01-29 14:36:54 -05:00
|
|
|
this._userVerifier.call_begin_verification(serviceName, this._cancellable, (obj, result) => {
|
|
|
|
try {
|
|
|
|
obj.call_begin_verification_finish(result);
|
|
|
|
} catch (e) {
|
|
|
|
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
|
|
|
|
return;
|
|
|
|
this._reportInitError('Failed to start verification', e);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this._hold.release();
|
|
|
|
});
|
2013-08-21 18:05:55 -04:00
|
|
|
}
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2012-07-17 14:54:07 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_beginVerification() {
|
2013-08-16 10:29:26 -04:00
|
|
|
this._startService(this._getForegroundService());
|
|
|
|
|
2013-07-29 14:18:30 -04:00
|
|
|
if (this._userName && this._haveFingerprintReader && !this.serviceIsForeground(FINGERPRINT_SERVICE_NAME))
|
2013-08-16 10:29:26 -04:00
|
|
|
this._startService(FINGERPRINT_SERVICE_NAME);
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2012-07-17 14:54:07 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_onInfo(client, serviceName, info) {
|
2013-07-29 14:23:45 -04:00
|
|
|
if (this.serviceIsForeground(serviceName)) {
|
2013-08-19 12:00:33 -04:00
|
|
|
this._queueMessage(info, MessageType.INFO);
|
2013-07-29 14:23:45 -04:00
|
|
|
} else if (serviceName == FINGERPRINT_SERVICE_NAME &&
|
2012-07-17 14:54:07 -04:00
|
|
|
this._haveFingerprintReader) {
|
2013-07-29 14:23:45 -04:00
|
|
|
// We don't show fingerprint messages directly since it's
|
|
|
|
// not the main auth service. Instead we use the messages
|
|
|
|
// as a cue to display our own message.
|
2012-08-19 20:15:18 -04:00
|
|
|
|
|
|
|
// Translators: this message is shown below the password entry field
|
|
|
|
// to indicate the user can swipe their finger instead
|
2013-08-19 12:00:33 -04:00
|
|
|
this._queueMessage(_("(or swipe finger)"), MessageType.HINT);
|
2012-07-17 14:54:07 -04:00
|
|
|
}
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2012-07-17 14:54:07 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_onProblem(client, serviceName, problem) {
|
util: abstract out default auth service in code
Right now, the primary way a user logs in is with
a password. They can also swipe their finger, if their
fingerprint is enrolled, but it's expected the fingerprint
auth service won't ask questions the user has to respond to
by typing. As such, we ignore questions that comes from
anything but the main auth service: gdm-password.
In the future, if a user inserts a smartcard, we'll want
to treat the gdm-smartcard service as the main auth service,
and let any questions from it get to the user.
This commit tries to prepare for that eventuality by storing
the name of the default auth service away in a _defaultService variable
before verification has begun, and then later checking incoming
queries against that service instead of checking against
string 'gdm-password' directly.
Of course, right now, _defaultService is always gdm-password.
https://bugzilla.gnome.org/show_bug.cgi?id=683437
2013-07-28 19:42:26 -04:00
|
|
|
if (!this.serviceIsForeground(serviceName))
|
2012-07-17 14:54:07 -04:00
|
|
|
return;
|
util: abstract out default auth service in code
Right now, the primary way a user logs in is with
a password. They can also swipe their finger, if their
fingerprint is enrolled, but it's expected the fingerprint
auth service won't ask questions the user has to respond to
by typing. As such, we ignore questions that comes from
anything but the main auth service: gdm-password.
In the future, if a user inserts a smartcard, we'll want
to treat the gdm-smartcard service as the main auth service,
and let any questions from it get to the user.
This commit tries to prepare for that eventuality by storing
the name of the default auth service away in a _defaultService variable
before verification has begun, and then later checking incoming
queries against that service instead of checking against
string 'gdm-password' directly.
Of course, right now, _defaultService is always gdm-password.
https://bugzilla.gnome.org/show_bug.cgi?id=683437
2013-07-28 19:42:26 -04:00
|
|
|
|
2013-08-19 12:00:33 -04:00
|
|
|
this._queueMessage(problem, MessageType.ERROR);
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2012-07-17 14:54:07 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_onInfoQuery(client, serviceName, question) {
|
util: abstract out default auth service in code
Right now, the primary way a user logs in is with
a password. They can also swipe their finger, if their
fingerprint is enrolled, but it's expected the fingerprint
auth service won't ask questions the user has to respond to
by typing. As such, we ignore questions that comes from
anything but the main auth service: gdm-password.
In the future, if a user inserts a smartcard, we'll want
to treat the gdm-smartcard service as the main auth service,
and let any questions from it get to the user.
This commit tries to prepare for that eventuality by storing
the name of the default auth service away in a _defaultService variable
before verification has begun, and then later checking incoming
queries against that service instead of checking against
string 'gdm-password' directly.
Of course, right now, _defaultService is always gdm-password.
https://bugzilla.gnome.org/show_bug.cgi?id=683437
2013-07-28 19:42:26 -04:00
|
|
|
if (!this.serviceIsForeground(serviceName))
|
2012-07-17 14:54:07 -04:00
|
|
|
return;
|
|
|
|
|
|
|
|
this.emit('ask-question', serviceName, question, '');
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2012-07-17 14:54:07 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_onSecretInfoQuery(client, serviceName, secretQuestion) {
|
util: abstract out default auth service in code
Right now, the primary way a user logs in is with
a password. They can also swipe their finger, if their
fingerprint is enrolled, but it's expected the fingerprint
auth service won't ask questions the user has to respond to
by typing. As such, we ignore questions that comes from
anything but the main auth service: gdm-password.
In the future, if a user inserts a smartcard, we'll want
to treat the gdm-smartcard service as the main auth service,
and let any questions from it get to the user.
This commit tries to prepare for that eventuality by storing
the name of the default auth service away in a _defaultService variable
before verification has begun, and then later checking incoming
queries against that service instead of checking against
string 'gdm-password' directly.
Of course, right now, _defaultService is always gdm-password.
https://bugzilla.gnome.org/show_bug.cgi?id=683437
2013-07-28 19:42:26 -04:00
|
|
|
if (!this.serviceIsForeground(serviceName))
|
2012-07-17 14:54:07 -04:00
|
|
|
return;
|
|
|
|
|
2013-10-10 04:21:47 -04:00
|
|
|
if (serviceName == OVIRT_SERVICE_NAME) {
|
|
|
|
// The only question asked by this service is "Token?"
|
|
|
|
this.answerQuery(serviceName, this._oVirtCredentialsManager.getToken());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-07-17 14:54:07 -04:00
|
|
|
this.emit('ask-question', serviceName, secretQuestion, '\u25cf');
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2012-07-17 14:54:07 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_onReset() {
|
2012-08-07 10:49:22 -04:00
|
|
|
// Clear previous attempts to authenticate
|
|
|
|
this._failCounter = 0;
|
util: abstract out default auth service in code
Right now, the primary way a user logs in is with
a password. They can also swipe their finger, if their
fingerprint is enrolled, but it's expected the fingerprint
auth service won't ask questions the user has to respond to
by typing. As such, we ignore questions that comes from
anything but the main auth service: gdm-password.
In the future, if a user inserts a smartcard, we'll want
to treat the gdm-smartcard service as the main auth service,
and let any questions from it get to the user.
This commit tries to prepare for that eventuality by storing
the name of the default auth service away in a _defaultService variable
before verification has begun, and then later checking incoming
queries against that service instead of checking against
string 'gdm-password' directly.
Of course, right now, _defaultService is always gdm-password.
https://bugzilla.gnome.org/show_bug.cgi?id=683437
2013-07-28 19:42:26 -04:00
|
|
|
this._updateDefaultService();
|
2012-07-17 14:54:07 -04:00
|
|
|
|
|
|
|
this.emit('reset');
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2012-07-17 14:54:07 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_onVerificationComplete() {
|
2012-07-17 14:54:07 -04:00
|
|
|
this.emit('verification-complete');
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2012-07-17 14:54:07 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_cancelAndReset() {
|
2013-03-18 00:59:56 -04:00
|
|
|
this.cancel();
|
|
|
|
this._onReset();
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2013-03-18 00:59:56 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_retry() {
|
2013-03-18 00:59:56 -04:00
|
|
|
this.begin(this._userName, new Batch.Hold());
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2013-03-18 00:59:56 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_verificationFailed(retry) {
|
2012-08-07 10:49:22 -04:00
|
|
|
// For Not Listed / enterprise logins, immediately reset
|
|
|
|
// the dialog
|
2018-05-28 20:10:09 -04:00
|
|
|
// Otherwise, when in login mode we allow ALLOWED_FAILURES attempts.
|
|
|
|
// After that, we go back to the welcome screen.
|
2012-08-07 10:49:22 -04:00
|
|
|
|
2012-10-29 12:40:55 -04:00
|
|
|
this._failCounter++;
|
2012-09-06 10:40:13 -04:00
|
|
|
let canRetry = retry && this._userName &&
|
2018-05-28 20:10:09 -04:00
|
|
|
(this._reauthOnly ||
|
|
|
|
this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY));
|
2012-09-06 10:40:13 -04:00
|
|
|
|
|
|
|
if (canRetry) {
|
2013-07-16 15:48:27 -04:00
|
|
|
if (!this.hasPendingMessages) {
|
2013-03-18 00:59:56 -04:00
|
|
|
this._retry();
|
|
|
|
} else {
|
2017-10-30 20:38:18 -04:00
|
|
|
let signalId = this.connect('no-more-messages', () => {
|
|
|
|
this.disconnect(signalId);
|
|
|
|
if (this._cancellable && !this._cancellable.is_cancelled())
|
|
|
|
this._retry();
|
|
|
|
});
|
2013-03-18 00:59:56 -04:00
|
|
|
}
|
2012-09-06 10:40:13 -04:00
|
|
|
} else {
|
2013-07-16 15:48:27 -04:00
|
|
|
if (!this.hasPendingMessages) {
|
2013-03-18 00:59:56 -04:00
|
|
|
this._cancelAndReset();
|
|
|
|
} else {
|
2017-10-30 20:38:18 -04:00
|
|
|
let signalId = this.connect('no-more-messages', () => {
|
|
|
|
this.disconnect(signalId);
|
|
|
|
this._cancelAndReset();
|
|
|
|
});
|
2013-03-18 00:59:56 -04:00
|
|
|
}
|
2012-08-07 10:49:22 -04:00
|
|
|
}
|
|
|
|
|
2018-05-28 20:00:04 -04:00
|
|
|
this.emit('verification-failed', canRetry);
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2012-08-07 10:49:22 -04:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_onConversationStopped(client, serviceName) {
|
2013-10-10 04:21:47 -04:00
|
|
|
// If the login failed with the preauthenticated oVirt credentials
|
|
|
|
// then discard the credentials and revert to default authentication
|
|
|
|
// mechanism.
|
|
|
|
if (this.serviceIsForeground(OVIRT_SERVICE_NAME)) {
|
|
|
|
this._oVirtCredentialsManager.resetToken();
|
|
|
|
this._preemptingService = null;
|
|
|
|
this._verificationFailed(false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-07-17 14:54:07 -04:00
|
|
|
// if the password service fails, then cancel everything.
|
|
|
|
// But if, e.g., fingerprint fails, still give
|
|
|
|
// password authentication a chance to succeed
|
util: abstract out default auth service in code
Right now, the primary way a user logs in is with
a password. They can also swipe their finger, if their
fingerprint is enrolled, but it's expected the fingerprint
auth service won't ask questions the user has to respond to
by typing. As such, we ignore questions that comes from
anything but the main auth service: gdm-password.
In the future, if a user inserts a smartcard, we'll want
to treat the gdm-smartcard service as the main auth service,
and let any questions from it get to the user.
This commit tries to prepare for that eventuality by storing
the name of the default auth service away in a _defaultService variable
before verification has begun, and then later checking incoming
queries against that service instead of checking against
string 'gdm-password' directly.
Of course, right now, _defaultService is always gdm-password.
https://bugzilla.gnome.org/show_bug.cgi?id=683437
2013-07-28 19:42:26 -04:00
|
|
|
if (this.serviceIsForeground(serviceName)) {
|
2012-09-06 10:40:13 -04:00
|
|
|
this._verificationFailed(true);
|
2012-08-19 21:37:54 -04:00
|
|
|
}
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
|
|
|
};
|
2012-07-17 14:54:07 -04:00
|
|
|
Signals.addSignalMethods(ShellUserVerifier.prototype);
|