[gnome-shell] gdm: support pre-authenticated logins from oVirt



commit 4cda61a16a49fee44cb2112b25931647d7d7141e
Author: Vinzenz Feenstra <evilissimo redhat com>
Date:   Thu Oct 10 10:21:47 2013 +0200

    gdm: support pre-authenticated logins from oVirt
    
    oVirt is software for managing medium-to-large scale deployments of
    virtual machine guests across multiple hosts. It supports a feature
    where users can authenticate with a central server and get
    transparently connected to a guest system and then automatically get logged
    into that guest to an associated user session.
    
    Guests using old versions of GDM support this single-sign-on capability
    by means of a greeter plugin, using the old greeter's extension
    API.
    
    This commit adds similar support to the gnome-shell based login screen.
    
    How it works:
    
    * The OVirtCredentialsManager singleton listens for
    
      'org.ovirt.vdsm.Credentials.UserAuthenticated'
    
      D-Bus signal on the system bus from the
    
      'org.ovirt.vdsm.Credentials'
    
      bus name. The service that provides that bus name is called
      the oVirt guest agent. It is also responsible for interacting
      with the the central server to get user credentials.
    
    * This UserAuthenticated signal passes, as a parameter, the a token
      which needs to be passed through to the PAM service that is specifically
      set up to integrate with the oVirt authentication architecture.
      The singleton object keeps the token internally so it can be queried
      later on.
    
    * The OVirtCredentialsManager emits a signal 'user-authenticated' on
      it's object once the dbus signal is triggered
    
    * When the 'user-authenticated' signal is emitted, the login screen
      tells GDM to start user verification using the PAM service. The
      authentication stack of the service includes a PAM module
      provided by oVirt that securely retrieves user credentials
      from the oVirt guest agent. The PAM module then forwards those
      credentials on to other modules in the stack so, e.g.,
      the user's gnome keyring can be automatically unlocked.
    
    * In case of the screen shield being visible, it also will react on that
      'user-authenticated' signal and lift the shield.
      In that case the login screen will check on construction time if
      the signal has already been triggered, and a token is available.
      If a token is available it will immediately trigger the functionality
      as described above.
    
    Signed-off-by: Vinzenz Feenstra <evilissimo redhat com>
    
    https://bugzilla.gnome.org/show_bug.cgi?id=702162

 js/Makefile.am        |    1 +
 js/gdm/authPrompt.js  |   13 ++++++++--
 js/gdm/oVirt.js       |   62 +++++++++++++++++++++++++++++++++++++++++++++++++
 js/gdm/util.js        |   31 ++++++++++++++++++++++++
 js/ui/screenShield.js |    8 ++++++
 5 files changed, 112 insertions(+), 3 deletions(-)
---
diff --git a/js/Makefile.am b/js/Makefile.am
index 7df786f..d614f4f 100644
--- a/js/Makefile.am
+++ b/js/Makefile.am
@@ -21,6 +21,7 @@ nobase_dist_js_DATA =         \
        gdm/batch.js            \
        gdm/fingerprint.js      \
        gdm/loginDialog.js      \
+       gdm/oVirt.js            \
        gdm/realmd.js           \
        gdm/util.js             \
        extensionPrefs/main.js  \
diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js
index fe2cf2d..1880e36 100644
--- a/js/gdm/authPrompt.js
+++ b/js/gdm/authPrompt.js
@@ -59,6 +59,7 @@ const AuthPrompt = new Lang.Class({
         this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
         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() {
@@ -219,6 +220,11 @@ const AuthPrompt = new Lang.Class({
         this.emit('prompted');
     },
 
+    _onOVirtUserAuthenticated: function() {
+        if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED)
+            this.reset();
+    },
+
     _onSmartcardStatusChanged: function() {
         this.smartcardDetected = this._userVerifier.smartcardDetected;
 
@@ -444,10 +450,11 @@ const AuthPrompt = new Lang.Class({
             // 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.smartcardDetected &&
-                   this._userVerifier.serviceIsForeground(GdmUtil.SMARTCARD_SERVICE_NAME)) {
+        } 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.
+            // 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.
diff --git a/js/gdm/oVirt.js b/js/gdm/oVirt.js
new file mode 100644
index 0000000..1a31f43
--- /dev/null
+++ b/js/gdm/oVirt.js
@@ -0,0 +1,62 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const Gio = imports.gi.Gio;
+const Lang = imports.lang;
+const Signals = imports.signals;
+
+const OVirtCredentialsIface = <interface name='org.ovirt.vdsm.Credentials'>
+<signal name="UserAuthenticated">
+    <arg type="s" name="token"/>
+</signal>
+</interface>;
+
+const OVirtCredentialsInfo = Gio.DBusInterfaceInfo.new_for_xml(OVirtCredentialsIface);
+
+let _oVirtCredentialsManager = null;
+
+function OVirtCredentials() {
+    var self = new Gio.DBusProxy({ g_connection: Gio.DBus.system,
+                                   g_interface_name: OVirtCredentialsInfo.name,
+                                   g_interface_info: OVirtCredentialsInfo,
+                                   g_name: 'org.ovirt.vdsm.Credentials',
+                                   g_object_path: '/org/ovirt/vdsm/Credentials',
+                                   g_flags: (Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
+    self.init(null);
+    return self;
+}
+
+const OVirtCredentialsManager = new Lang.Class({
+    Name: 'OVirtCredentialsManager',
+    _init: function() {
+        this._token = null;
+
+        this._credentials = new OVirtCredentials();
+        this._credentials.connectSignal('UserAuthenticated',
+                                        Lang.bind(this, this._onUserAuthenticated));
+    },
+
+    _onUserAuthenticated: function(proxy, sender, [token]) {
+        this._token = token;
+        this.emit('user-authenticated', token);
+    },
+
+    hasToken: function() {
+        return this._token != null;
+    },
+
+    getToken: function() {
+        return this._token;
+    },
+
+    resetToken: function() {
+        this._token = null;
+    }
+});
+Signals.addSignalMethods(OVirtCredentialsManager.prototype);
+
+function getOVirtCredentialsManager() {
+    if (!_oVirtCredentialsManager)
+        _oVirtCredentialsManager = new OVirtCredentialsManager();
+
+    return _oVirtCredentialsManager;
+}
diff --git a/js/gdm/util.js b/js/gdm/util.js
index f421902..66852fd 100644
--- a/js/gdm/util.js
+++ b/js/gdm/util.js
@@ -10,6 +10,7 @@ const St = imports.gi.St;
 
 const Batch = imports.gdm.batch;
 const Fprint = imports.gdm.fingerprint;
+const OVirt = imports.gdm.oVirt;
 const Main = imports.ui.main;
 const Params = imports.misc.params;
 const ShellEntry = imports.ui.shellEntry;
@@ -19,6 +20,7 @@ const Tweener = imports.ui.tweener;
 const PASSWORD_SERVICE_NAME = 'gdm-password';
 const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
 const SMARTCARD_SERVICE_NAME = 'gdm-smartcard';
+const OVIRT_SERVICE_NAME = 'gdm-ovirtcred';
 const FADE_ANIMATION_TIME = 0.16;
 const CLONE_FADE_ANIMATION_TIME = 0.25;
 
@@ -151,6 +153,14 @@ const ShellUserVerifier = new Lang.Class({
         this.reauthenticating = false;
 
         this._failCounter = 0;
+
+        this._oVirtCredentialsManager = OVirt.getOVirtCredentialsManager();
+
+        if (this._oVirtCredentialsManager.hasToken())
+            this._oVirtUserAuthenticated(this._oVirtCredentialsManager.getToken());
+
+        this._oVirtCredentialsManager.connect('user-authenticated',
+                                              Lang.bind(this, this._oVirtUserAuthenticated));
     },
 
     begin: function(userName, hold) {
@@ -277,6 +287,11 @@ const ShellUserVerifier = new Lang.Class({
             }));
     },
 
+    _oVirtUserAuthenticated: function(token) {
+        this._preemptingService = OVIRT_SERVICE_NAME;
+        this.emit('ovirt-user-authenticated');
+    },
+
     _checkForSmartcard: function() {
         let smartcardDetected;
 
@@ -455,6 +470,12 @@ const ShellUserVerifier = new Lang.Class({
         if (!this.serviceIsForeground(serviceName))
             return;
 
+        if (serviceName == OVIRT_SERVICE_NAME) {
+            // The only question asked by this service is "Token?"
+            this.answerQuery(serviceName, this._oVirtCredentialsManager.getToken());
+            return;
+        }
+
         this.emit('ask-question', serviceName, secretQuestion, '\u25cf');
     },
 
@@ -515,6 +536,16 @@ const ShellUserVerifier = new Lang.Class({
     },
 
     _onConversationStopped: function(client, serviceName) {
+        // 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;
+        }
+
         // if the password service fails, then cancel everything.
         // But if, e.g., fingerprint fails, still give
         // password authentication a chance to succeed
diff --git a/js/ui/screenShield.js b/js/ui/screenShield.js
index 32fd737..731122a 100644
--- a/js/ui/screenShield.js
+++ b/js/ui/screenShield.js
@@ -19,6 +19,7 @@ const Background = imports.ui.background;
 const GnomeSession = imports.misc.gnomeSession;
 const Hash = imports.misc.hash;
 const Layout = imports.ui.layout;
+const OVirt = imports.gdm.oVirt;
 const LoginManager = imports.misc.loginManager;
 const Lightbox = imports.ui.lightbox;
 const Main = imports.ui.main;
@@ -545,6 +546,13 @@ const ScreenShield = new Lang.Class({
                                                this._liftShield(true, 0);
                                        }));
 
+        this._oVirtCredentialsManager = OVirt.getOVirtCredentialsManager();
+        this._oVirtCredentialsManager.connect('user-authenticated',
+                                              Lang.bind(this, function() {
+                                                  if (this._isLocked)
+                                                      this._liftShield(true, 0);
+                                              }));
+
         this._inhibitor = null;
         this._aboutToSuspend = false;
         this._loginManager = LoginManager.getLoginManager();


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]