Add a PolicyKit authentication agent
A PolicyKit Authentication Agent is a construct used to authenticate one or more identities. See the PolicyKit documentation for more details on authentication agents and how PolicyKit works: http://hal.freedesktop.org/docs/polkit/ Since gjs does not support subclassing a GObject class from Javascript code, we bring in a native class to bridge the VFuncs to GObject signals. Additionally, this native class also queues up authentication requests so the user of the native class only has to deal with a single outstanding request at any one time. The file js/ui/polkitAuthenticationAgent.js introduces a singleton that listens for authentication requests via the native class. This singleton uses the PolkitAgent machinery to do the actual heavy-weight lifting required for authentication (essentially a PAM conversation). We currently don't allow the user to pick the identity to be authenticated. https://bugzilla.gnome.org/show_bug.cgi?id=642886 Signed-off-by: David Zeuthen <davidz@redhat.com>
This commit is contained in:
parent
1fcfadd53c
commit
86b925a294
@ -71,7 +71,7 @@ LIBEDATASERVER_REQUIRED=1.2.0
|
|||||||
LIBEDATASERVERUI2_REQUIRED=1.2.0
|
LIBEDATASERVERUI2_REQUIRED=1.2.0
|
||||||
LIBEDATASERVERUI3_REQUIRED=2.91.6
|
LIBEDATASERVERUI3_REQUIRED=2.91.6
|
||||||
TELEPATHY_GLIB_MIN_VERSION=0.13.12
|
TELEPATHY_GLIB_MIN_VERSION=0.13.12
|
||||||
|
POLKIT_MIN_VERSION=0.100
|
||||||
|
|
||||||
# Collect more than 20 libraries for a prize!
|
# Collect more than 20 libraries for a prize!
|
||||||
PKG_CHECK_MODULES(MUTTER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION
|
PKG_CHECK_MODULES(MUTTER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION
|
||||||
@ -86,7 +86,8 @@ PKG_CHECK_MODULES(MUTTER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION
|
|||||||
libstartup-notification-1.0
|
libstartup-notification-1.0
|
||||||
gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
|
gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
|
||||||
libcanberra
|
libcanberra
|
||||||
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION)
|
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
|
||||||
|
polkit-agent-1 >= $POLKIT_MIN_VERSION)
|
||||||
|
|
||||||
GJS_VERSION=`$PKG_CONFIG --modversion gjs-internals-1.0`
|
GJS_VERSION=`$PKG_CONFIG --modversion gjs-internals-1.0`
|
||||||
AC_DEFINE_UNQUOTED([GJS_VERSION], ["$GJS_VERSION"], [The version of GJS we're linking to])
|
AC_DEFINE_UNQUOTED([GJS_VERSION], ["$GJS_VERSION"], [The version of GJS we're linking to])
|
||||||
|
@ -1347,6 +1347,78 @@ StTooltip StLabel {
|
|||||||
color: #444444;
|
color: #444444;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* PolicyKit Authentication Dialog */
|
||||||
|
.polkit-dialog {
|
||||||
|
/* this is the width of the entire modal popup */
|
||||||
|
width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.polkit-dialog-main-layout {
|
||||||
|
spacing: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.polkit-dialog-message-layout {
|
||||||
|
spacing: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.polkit-dialog-headline {
|
||||||
|
font-size: 12pt;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.polkit-dialog-description {
|
||||||
|
font-size: 10pt;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.polkit-dialog-user-layout {
|
||||||
|
padding-left: 10px;
|
||||||
|
spacing: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.polkit-dialog-password-label {
|
||||||
|
padding-right: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.polkit-dialog-password-entry {
|
||||||
|
background-color: white;
|
||||||
|
color: black;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.polkit-dialog-error-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.polkit-dialog-error-box {
|
||||||
|
padding-top: 15px;
|
||||||
|
spacing: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.polkit-dialog-checking-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.polkit-dialog-checking-box {
|
||||||
|
padding-top: 15px;
|
||||||
|
spacing: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.polkit-dialog-info-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.polkit-dialog-info-box {
|
||||||
|
padding-top: 15px;
|
||||||
|
spacing: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Magnifier */
|
/* Magnifier */
|
||||||
|
|
||||||
.magnifier-zoom-region {
|
.magnifier-zoom-region {
|
||||||
|
@ -39,6 +39,7 @@ nobase_dist_js_DATA = \
|
|||||||
ui/panel.js \
|
ui/panel.js \
|
||||||
ui/panelMenu.js \
|
ui/panelMenu.js \
|
||||||
ui/placeDisplay.js \
|
ui/placeDisplay.js \
|
||||||
|
ui/polkitAuthenticationAgent.js \
|
||||||
ui/popupMenu.js \
|
ui/popupMenu.js \
|
||||||
ui/runDialog.js \
|
ui/runDialog.js \
|
||||||
ui/scripting.js \
|
ui/scripting.js \
|
||||||
|
@ -23,6 +23,7 @@ const _ = Gettext.gettext;
|
|||||||
const Chrome = imports.ui.chrome;
|
const Chrome = imports.ui.chrome;
|
||||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||||
const EndSessionDialog = imports.ui.endSessionDialog;
|
const EndSessionDialog = imports.ui.endSessionDialog;
|
||||||
|
const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent;
|
||||||
const Environment = imports.ui.environment;
|
const Environment = imports.ui.environment;
|
||||||
const ExtensionSystem = imports.ui.extensionSystem;
|
const ExtensionSystem = imports.ui.extensionSystem;
|
||||||
const MessageTray = imports.ui.messageTray;
|
const MessageTray = imports.ui.messageTray;
|
||||||
@ -183,6 +184,9 @@ function start() {
|
|||||||
// initiate logouts.
|
// initiate logouts.
|
||||||
EndSessionDialog.init();
|
EndSessionDialog.init();
|
||||||
|
|
||||||
|
// Attempt to become a PolicyKit authentication agent
|
||||||
|
PolkitAuthenticationAgent.init()
|
||||||
|
|
||||||
global.gdk_screen.connect('monitors-changed', _relayout);
|
global.gdk_screen.connect('monitors-changed', _relayout);
|
||||||
|
|
||||||
ExtensionSystem.init();
|
ExtensionSystem.init();
|
||||||
|
350
js/ui/polkitAuthenticationAgent.js
Normal file
350
js/ui/polkitAuthenticationAgent.js
Normal file
@ -0,0 +1,350 @@
|
|||||||
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||||
|
*
|
||||||
|
* Copyright 2010 Red Hat, Inc
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
* Author: David Zeuthen <davidz@redhat.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
const Gettext = imports.gettext.domain('gnome-shell');
|
||||||
|
const _ = Gettext.gettext;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
const Pango = imports.gi.Pango;
|
||||||
|
const Gdm = imports.gi.Gdm;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const Mainloop = imports.mainloop;
|
||||||
|
const Polkit = imports.gi.Polkit;
|
||||||
|
const PolkitAgent = imports.gi.PolkitAgent;
|
||||||
|
|
||||||
|
const ModalDialog = imports.ui.modalDialog;
|
||||||
|
|
||||||
|
function AuthenticationDialog(message, cookie, userNames) {
|
||||||
|
this._init(message, cookie, userNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
AuthenticationDialog.prototype = {
|
||||||
|
__proto__: ModalDialog.ModalDialog.prototype,
|
||||||
|
|
||||||
|
_init: function(message, cookie, userNames) {
|
||||||
|
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'polkit-dialog' });
|
||||||
|
|
||||||
|
this.message = message;
|
||||||
|
this.userNames = userNames;
|
||||||
|
|
||||||
|
let mainContentBox = new St.BoxLayout({ style_class: 'polkit-dialog-main-layout',
|
||||||
|
vertical: false });
|
||||||
|
this.contentLayout.add(mainContentBox,
|
||||||
|
{ x_fill: true,
|
||||||
|
y_fill: true });
|
||||||
|
|
||||||
|
let icon = new St.Icon({ icon_name: 'dialog-password-symbolic' });
|
||||||
|
mainContentBox.add(icon,
|
||||||
|
{ x_fill: true,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.END,
|
||||||
|
y_align: St.Align.START });
|
||||||
|
|
||||||
|
let messageBox = new St.BoxLayout({ style_class: 'polkit-dialog-message-layout',
|
||||||
|
vertical: true });
|
||||||
|
mainContentBox.add(messageBox,
|
||||||
|
{ y_align: St.Align.START });
|
||||||
|
|
||||||
|
this._subjectLabel = new St.Label({ style_class: 'polkit-dialog-headline',
|
||||||
|
text: _('Authentication Required') });
|
||||||
|
|
||||||
|
messageBox.add(this._subjectLabel,
|
||||||
|
{ y_fill: false,
|
||||||
|
y_align: St.Align.START });
|
||||||
|
|
||||||
|
this._descriptionLabel = new St.Label({ style_class: 'polkit-dialog-description',
|
||||||
|
text: message });
|
||||||
|
this._descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||||
|
this._descriptionLabel.clutter_text.line_wrap = true;
|
||||||
|
|
||||||
|
messageBox.add(this._descriptionLabel,
|
||||||
|
{ y_fill: true,
|
||||||
|
y_align: St.Align.START });
|
||||||
|
|
||||||
|
if (userNames.length > 1) {
|
||||||
|
log('polkitAuthenticationAgent: Received ' + userNames.length +
|
||||||
|
' identities that can be used for authentication. Only ' +
|
||||||
|
'considering the first one.');
|
||||||
|
}
|
||||||
|
|
||||||
|
let userName = userNames[0];
|
||||||
|
|
||||||
|
this._user = Gdm.UserManager.ref_default().get_user(userName);
|
||||||
|
let userRealName = this._user.get_real_name()
|
||||||
|
this._userLoadedId = this._user.connect('notify::is_loaded',
|
||||||
|
Lang.bind(this, this._onUserChanged));
|
||||||
|
this._userChangedId = this._user.connect('changed',
|
||||||
|
Lang.bind(this, this._onUserChanged));
|
||||||
|
|
||||||
|
// Special case 'root'
|
||||||
|
if (userName == 'root')
|
||||||
|
userRealName = _('Administrator');
|
||||||
|
|
||||||
|
// Work around Gdm.UserManager returning an empty string for the real name
|
||||||
|
if (userRealName.length == 0)
|
||||||
|
userRealName = userName;
|
||||||
|
|
||||||
|
let userBox = new St.BoxLayout({ style_class: 'polkit-dialog-user-layout',
|
||||||
|
vertical: false });
|
||||||
|
messageBox.add(userBox);
|
||||||
|
|
||||||
|
this._userIcon = new St.Icon();
|
||||||
|
this._userIcon.hide();
|
||||||
|
userBox.add(this._userIcon,
|
||||||
|
{ x_fill: true,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.END,
|
||||||
|
y_align: St.Align.START });
|
||||||
|
|
||||||
|
let userLabel = new St.Label(({ style_class: 'polkit-dialog-user-label',
|
||||||
|
text: userRealName }));
|
||||||
|
userBox.add(userLabel,
|
||||||
|
{ x_fill: true,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.END,
|
||||||
|
y_align: St.Align.MIDDLE });
|
||||||
|
|
||||||
|
this._onUserChanged();
|
||||||
|
|
||||||
|
this._passwordBox = new St.BoxLayout({ vertical: false });
|
||||||
|
messageBox.add(this._passwordBox);
|
||||||
|
this._passwordLabel = new St.Label(({ style_class: 'polkit-dialog-password-label' }));
|
||||||
|
this._passwordBox.add(this._passwordLabel);
|
||||||
|
this._passwordEntry = new St.Entry({ style_class: 'polkit-dialog-password-entry',
|
||||||
|
text: _(''),
|
||||||
|
can_focus: true});
|
||||||
|
this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivate));
|
||||||
|
this._passwordBox.add(this._passwordEntry,
|
||||||
|
{expand: true });
|
||||||
|
this._passwordBox.hide();
|
||||||
|
|
||||||
|
this._errorBox = new St.BoxLayout({ style_class: 'polkit-dialog-error-box' });
|
||||||
|
messageBox.add(this._errorBox);
|
||||||
|
let errorIcon = new St.Icon({ icon_name: 'dialog-error',
|
||||||
|
icon_size: 24,
|
||||||
|
style_class: 'polkit-dialog-error-icon' });
|
||||||
|
this._errorBox.add(errorIcon, { y_align: St.Align.MIDDLE });
|
||||||
|
this._errorMessage = new St.Label({ style_class: 'polkit-dialog-error-label' });
|
||||||
|
this._errorMessage.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||||
|
this._errorMessage.clutter_text.line_wrap = true;
|
||||||
|
this._errorBox.add(this._errorMessage, { expand: true,
|
||||||
|
y_align: St.Align.MIDDLE,
|
||||||
|
y_fill: true });
|
||||||
|
this._errorBox.hide();
|
||||||
|
|
||||||
|
this._infoBox = new St.BoxLayout({ style_class: 'polkit-dialog-info-box' });
|
||||||
|
messageBox.add(this._infoBox);
|
||||||
|
let infoIcon = new St.Icon({ icon_name: 'dialog-information',
|
||||||
|
icon_size: 24,
|
||||||
|
style_class: 'polkit-dialog-info-icon' });
|
||||||
|
this._infoBox.add(infoIcon, { y_align: St.Align.MIDDLE });
|
||||||
|
this._infoMessage = new St.Label({ style_class: 'polkit-dialog-info-label'});
|
||||||
|
this._infoMessage.clutter_text.line_wrap = true;
|
||||||
|
this._infoBox.add(this._infoMessage, { expand: true,
|
||||||
|
y_align: St.Align.MIDDLE,
|
||||||
|
y_fill: true });
|
||||||
|
this._infoBox.hide();
|
||||||
|
|
||||||
|
this.setButtons([{ label: _('Cancel'),
|
||||||
|
action: Lang.bind(this, this.cancel),
|
||||||
|
key: Clutter.Escape
|
||||||
|
},
|
||||||
|
{ label: _('Authenticate'),
|
||||||
|
action: Lang.bind(this, this._onAuthenticateButtonPressed)
|
||||||
|
}]);
|
||||||
|
|
||||||
|
this._doneEmitted = false;
|
||||||
|
|
||||||
|
this._identityToAuth = Polkit.UnixUser.new_for_name(userName);
|
||||||
|
this._cookie = cookie;
|
||||||
|
|
||||||
|
this._session = new PolkitAgent.Session({ identity: this._identityToAuth,
|
||||||
|
cookie: this._cookie });
|
||||||
|
this._session.connect('completed', Lang.bind(this, this._onSessionCompleted));
|
||||||
|
this._session.connect('request', Lang.bind(this, this._onSessionRequest));
|
||||||
|
this._session.connect('show-error', Lang.bind(this, this._onSessionShowError));
|
||||||
|
this._session.connect('show-info', Lang.bind(this, this._onSessionShowInfo));
|
||||||
|
this.connect('opened',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this._session.initiate();
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_emitDone: function(keepVisible) {
|
||||||
|
if (!this._doneEmitted) {
|
||||||
|
this._doneEmitted = true;
|
||||||
|
this.emit('done', keepVisible);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onEntryActivate: function() {
|
||||||
|
let response = this._passwordEntry.get_text();
|
||||||
|
this._session.response(response);
|
||||||
|
// When the user responds, dismiss already shown info and
|
||||||
|
// error texts (if any)
|
||||||
|
this._errorBox.hide();
|
||||||
|
this._infoBox.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onAuthenticateButtonPressed: function() {
|
||||||
|
this._onEntryActivate();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onSessionCompleted: function(session, gainedAuthorization) {
|
||||||
|
this._passwordBox.hide();
|
||||||
|
this._emitDone(!gainedAuthorization);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onSessionRequest: function(session, request, echo_on) {
|
||||||
|
// Cheap localization trick
|
||||||
|
if (request == 'Password:')
|
||||||
|
this._passwordLabel.set_text(_('Password:'));
|
||||||
|
else
|
||||||
|
this._passwordLabel.set_text(request);
|
||||||
|
|
||||||
|
if (echo_on)
|
||||||
|
this._passwordEntry.clutter_text.set_password_char('');
|
||||||
|
else
|
||||||
|
this._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
|
||||||
|
|
||||||
|
this._passwordBox.show();
|
||||||
|
this._passwordEntry.set_text('');
|
||||||
|
this._passwordEntry.grab_key_focus();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onSessionShowError: function(session, text) {
|
||||||
|
this._passwordEntry.set_text('');
|
||||||
|
this._errorMessage.set_text(text);
|
||||||
|
this._errorBox.show();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onSessionShowInfo: function(session, text) {
|
||||||
|
this._passwordEntry.set_text('');
|
||||||
|
this._infoMessage.set_text(text);
|
||||||
|
this._infoBox.show();
|
||||||
|
},
|
||||||
|
|
||||||
|
destroySession: function() {
|
||||||
|
if (this._session) {
|
||||||
|
this._session.cancel();
|
||||||
|
this._session = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onUserChanged: function() {
|
||||||
|
if (this._user.is_loaded) {
|
||||||
|
let iconFileName = this._user.get_icon_file();
|
||||||
|
let iconFile = Gio.file_new_for_path(iconFileName);
|
||||||
|
let icon;
|
||||||
|
if (iconFile.query_exists(null)) {
|
||||||
|
icon = new Gio.FileIcon({file: iconFile});
|
||||||
|
} else {
|
||||||
|
icon = new Gio.ThemedIcon({name: 'avatar-default'});
|
||||||
|
}
|
||||||
|
this._userIcon.set_gicon (icon);
|
||||||
|
this._userIcon.show();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
cancel: function() {
|
||||||
|
this.close(global.get_current_time());
|
||||||
|
this._emitDone(false);
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
Signals.addSignalMethods(AuthenticationDialog.prototype);
|
||||||
|
|
||||||
|
function AuthenticationAgent() {
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
AuthenticationAgent.prototype = {
|
||||||
|
_init: function() {
|
||||||
|
this._native = new Shell.PolkitAuthenticationAgent();
|
||||||
|
this._native.connect('initiate', Lang.bind(this, this._onInitiate));
|
||||||
|
this._native.connect('cancel', Lang.bind(this, this._onCancel));
|
||||||
|
this._currentDialog = null;
|
||||||
|
this._isCompleting = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onInitiate: function(nativeAgent, actionId, message, iconName, cookie, userNames) {
|
||||||
|
this._currentDialog = new AuthenticationDialog(message, cookie, userNames);
|
||||||
|
if (!this._currentDialog.open(global.get_current_time())) {
|
||||||
|
// This can fail if e.g. unable to get input grab
|
||||||
|
//
|
||||||
|
// In an ideal world this wouldn't happen (because the
|
||||||
|
// Shell is in complete control of the session) but that's
|
||||||
|
// just not how things work right now.
|
||||||
|
//
|
||||||
|
// We could add retrying if this turns out to be a problem
|
||||||
|
log('polkitAuthenticationAgent: Failed to show modal dialog');
|
||||||
|
this._currentDialog.destroySession();
|
||||||
|
this._currentDialog = null;
|
||||||
|
this._native.complete()
|
||||||
|
} else {
|
||||||
|
this._currentDialog.connect('done', Lang.bind(this, this._onDialogDone));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onCancel: function(nativeAgent) {
|
||||||
|
this._completeRequest(false);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onDialogDone: function(dialog, keepVisible) {
|
||||||
|
this._completeRequest(keepVisible);
|
||||||
|
},
|
||||||
|
|
||||||
|
_reallyCompleteRequest: function() {
|
||||||
|
this._currentDialog.close();
|
||||||
|
this._currentDialog.destroySession();
|
||||||
|
this._currentDialog = null;
|
||||||
|
this._isCompleting = false;
|
||||||
|
|
||||||
|
this._native.complete()
|
||||||
|
},
|
||||||
|
|
||||||
|
_completeRequest: function(keepVisible) {
|
||||||
|
if (this._isCompleting)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._isCompleting = true;
|
||||||
|
|
||||||
|
if (keepVisible) {
|
||||||
|
// Give the user 2 seconds to read 'Authentication Failure' before
|
||||||
|
// dismissing the dialog
|
||||||
|
Mainloop.timeout_add(2000,
|
||||||
|
Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
this._reallyCompleteRequest();
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
this._reallyCompleteRequest();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
let agent = new AuthenticationAgent();
|
||||||
|
}
|
@ -94,6 +94,8 @@ libgnome_shell_la_SOURCES = \
|
|||||||
shell-gtk-embed.c \
|
shell-gtk-embed.c \
|
||||||
shell-global.c \
|
shell-global.c \
|
||||||
shell-perf-log.c \
|
shell-perf-log.c \
|
||||||
|
shell-polkit-authentication-agent.h \
|
||||||
|
shell-polkit-authentication-agent.c \
|
||||||
shell-slicer.c \
|
shell-slicer.c \
|
||||||
shell-stack.c \
|
shell-stack.c \
|
||||||
shell-tray-icon.c \
|
shell-tray-icon.c \
|
||||||
|
@ -5,3 +5,4 @@ VOID:BOXED,OBJECT
|
|||||||
VOID:OBJECT,OBJECT
|
VOID:OBJECT,OBJECT
|
||||||
VOID:STRING,OBJECT,BOOLEAN
|
VOID:STRING,OBJECT,BOOLEAN
|
||||||
VOID:INT,INT
|
VOID:INT,INT
|
||||||
|
VOID:STRING,STRING,STRING,STRING,BOXED
|
||||||
|
424
src/shell-polkit-authentication-agent.c
Normal file
424
src/shell-polkit-authentication-agent.c
Normal file
@ -0,0 +1,424 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 20011 Red Hat, Inc.
|
||||||
|
*
|
||||||
|
* Author: David Zeuthen <davidz@redhat.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <pwd.h>
|
||||||
|
|
||||||
|
#include "shell-marshal.h"
|
||||||
|
|
||||||
|
#define POLKIT_AGENT_I_KNOW_API_IS_SUBJECT_TO_CHANGE
|
||||||
|
#include <polkitagent/polkitagent.h>
|
||||||
|
#include "shell-polkit-authentication-agent.h"
|
||||||
|
|
||||||
|
/* uncomment for useful debug output */
|
||||||
|
/* #define SHOW_DEBUG */
|
||||||
|
|
||||||
|
#ifdef SHOW_DEBUG
|
||||||
|
static void
|
||||||
|
print_debug (const gchar *format, ...)
|
||||||
|
{
|
||||||
|
gchar *s;
|
||||||
|
va_list ap;
|
||||||
|
gchar timebuf[64];
|
||||||
|
GTimeVal now;
|
||||||
|
time_t now_t;
|
||||||
|
struct tm broken_down;
|
||||||
|
|
||||||
|
g_get_current_time (&now);
|
||||||
|
now_t = now.tv_sec;
|
||||||
|
localtime_r (&now_t, &broken_down);
|
||||||
|
strftime (timebuf, sizeof timebuf, "%H:%M:%S", &broken_down);
|
||||||
|
|
||||||
|
va_start (ap, format);
|
||||||
|
s = g_strdup_vprintf (format, ap);
|
||||||
|
va_end (ap);
|
||||||
|
|
||||||
|
g_print ("ShellPolkitAuthenticationAgent: %s.%03d: %s\n", timebuf, (gint) (now.tv_usec / 1000), s);
|
||||||
|
g_free (s);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static void
|
||||||
|
print_debug (const gchar *str, ...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
struct _ShellPolkitAuthenticationAgentClass
|
||||||
|
{
|
||||||
|
PolkitAgentListenerClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _AuthRequest;
|
||||||
|
typedef struct _AuthRequest AuthRequest;
|
||||||
|
|
||||||
|
struct _ShellPolkitAuthenticationAgent {
|
||||||
|
PolkitAgentListener parent_instance;
|
||||||
|
|
||||||
|
GList *scheduled_requests;
|
||||||
|
|
||||||
|
AuthRequest *current_request;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Signals */
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
INITIATE_SIGNAL,
|
||||||
|
CANCEL_SIGNAL,
|
||||||
|
LAST_SIGNAL
|
||||||
|
};
|
||||||
|
|
||||||
|
static guint signals[LAST_SIGNAL] = { 0 };
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (ShellPolkitAuthenticationAgent, shell_polkit_authentication_agent, POLKIT_AGENT_TYPE_LISTENER);
|
||||||
|
|
||||||
|
static void initiate_authentication (PolkitAgentListener *listener,
|
||||||
|
const gchar *action_id,
|
||||||
|
const gchar *message,
|
||||||
|
const gchar *icon_name,
|
||||||
|
PolkitDetails *details,
|
||||||
|
const gchar *cookie,
|
||||||
|
GList *identities,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
|
||||||
|
static gboolean initiate_authentication_finish (PolkitAgentListener *listener,
|
||||||
|
GAsyncResult *res,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_polkit_authentication_agent_init (ShellPolkitAuthenticationAgent *agent)
|
||||||
|
{
|
||||||
|
gpointer handle;
|
||||||
|
PolkitSubject *subject;
|
||||||
|
GError *error;
|
||||||
|
|
||||||
|
subject = NULL;
|
||||||
|
|
||||||
|
error = NULL;
|
||||||
|
subject = polkit_unix_session_new_for_process_sync (getpid (),
|
||||||
|
NULL, /* GCancellable* */
|
||||||
|
&error);
|
||||||
|
if (subject == NULL)
|
||||||
|
{
|
||||||
|
g_warning ("Error getting session for the process we are in: %s (%s %d)",
|
||||||
|
error->message,
|
||||||
|
g_quark_to_string (error->domain),
|
||||||
|
error->code);
|
||||||
|
g_error_free (error);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
handle = polkit_agent_listener_register (POLKIT_AGENT_LISTENER (agent),
|
||||||
|
POLKIT_AGENT_REGISTER_FLAGS_NONE,
|
||||||
|
subject,
|
||||||
|
NULL, /* use default object path */
|
||||||
|
NULL, /* GCancellable */
|
||||||
|
&error);
|
||||||
|
if (handle == NULL)
|
||||||
|
{
|
||||||
|
g_warning ("Error registering polkit authentication agent: %s (%s %d)",
|
||||||
|
error->message,
|
||||||
|
g_quark_to_string (error->domain),
|
||||||
|
error->code);
|
||||||
|
g_error_free (error);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We don't need to register so skip saving handle */
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (subject != NULL)
|
||||||
|
g_object_unref (subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_polkit_authentication_agent_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
/* ShellPolkitAuthenticationAgent *agent = SHELL_POLKIT_AUTHENTICATION_AGENT (object); */
|
||||||
|
|
||||||
|
/* Specifically left empty since the object stays alive forever - if code
|
||||||
|
* is reused it would need to free outstanding requests etc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (shell_polkit_authentication_agent_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_polkit_authentication_agent_class_init (ShellPolkitAuthenticationAgentClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class;
|
||||||
|
PolkitAgentListenerClass *listener_class;
|
||||||
|
|
||||||
|
gobject_class = G_OBJECT_CLASS (klass);
|
||||||
|
gobject_class->finalize = shell_polkit_authentication_agent_finalize;
|
||||||
|
|
||||||
|
listener_class = POLKIT_AGENT_LISTENER_CLASS (klass);
|
||||||
|
listener_class->initiate_authentication = initiate_authentication;
|
||||||
|
listener_class->initiate_authentication_finish = initiate_authentication_finish;
|
||||||
|
|
||||||
|
signals[INITIATE_SIGNAL] =
|
||||||
|
g_signal_new ("initiate",
|
||||||
|
G_TYPE_FROM_CLASS (klass),
|
||||||
|
G_SIGNAL_RUN_LAST,
|
||||||
|
0, /* class_offset */
|
||||||
|
NULL, /* accumulator */
|
||||||
|
NULL, /* accumulator data */
|
||||||
|
_shell_marshal_VOID__STRING_STRING_STRING_STRING_BOXED,
|
||||||
|
G_TYPE_NONE,
|
||||||
|
5,
|
||||||
|
G_TYPE_STRING,
|
||||||
|
G_TYPE_STRING,
|
||||||
|
G_TYPE_STRING,
|
||||||
|
G_TYPE_STRING,
|
||||||
|
G_TYPE_STRV);
|
||||||
|
|
||||||
|
signals[CANCEL_SIGNAL] =
|
||||||
|
g_signal_new ("cancel",
|
||||||
|
G_TYPE_FROM_CLASS (klass),
|
||||||
|
G_SIGNAL_RUN_LAST,
|
||||||
|
0, /* class_offset */
|
||||||
|
NULL, /* accumulator */
|
||||||
|
NULL, /* accumulator data */
|
||||||
|
g_cclosure_marshal_VOID__VOID,
|
||||||
|
G_TYPE_NONE,
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ShellPolkitAuthenticationAgent *
|
||||||
|
shell_polkit_authentication_agent_new (void)
|
||||||
|
{
|
||||||
|
return SHELL_POLKIT_AUTHENTICATION_AGENT (g_object_new (SHELL_TYPE_POLKIT_AUTHENTICATION_AGENT, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct _AuthRequest {
|
||||||
|
/* not holding ref */
|
||||||
|
ShellPolkitAuthenticationAgent *agent;
|
||||||
|
GCancellable *cancellable;
|
||||||
|
gulong handler_id;
|
||||||
|
|
||||||
|
/* copies */
|
||||||
|
gchar *action_id;
|
||||||
|
gchar *message;
|
||||||
|
gchar *icon_name;
|
||||||
|
PolkitDetails *details;
|
||||||
|
gchar *cookie;
|
||||||
|
GList *identities;
|
||||||
|
|
||||||
|
GSimpleAsyncResult *simple;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
auth_request_free (AuthRequest *request)
|
||||||
|
{
|
||||||
|
g_cancellable_disconnect (request->cancellable, request->handler_id);
|
||||||
|
g_free (request->action_id);
|
||||||
|
g_free (request->message);
|
||||||
|
g_free (request->icon_name);
|
||||||
|
g_object_unref (request->details);
|
||||||
|
g_list_foreach (request->identities, (GFunc) g_object_unref, NULL);
|
||||||
|
g_list_free (request->identities);
|
||||||
|
g_object_unref (request->simple);
|
||||||
|
g_free (request);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
auth_request_initiate (AuthRequest *request)
|
||||||
|
{
|
||||||
|
gchar **user_names;
|
||||||
|
GPtrArray *p;
|
||||||
|
GList *l;
|
||||||
|
|
||||||
|
p = g_ptr_array_new ();
|
||||||
|
for (l = request->identities; l != NULL; l = l->next)
|
||||||
|
{
|
||||||
|
if (POLKIT_IS_UNIX_USER (l->data))
|
||||||
|
{
|
||||||
|
PolkitUnixUser *user = POLKIT_UNIX_USER (l->data);
|
||||||
|
gint uid;
|
||||||
|
gchar buf[4096];
|
||||||
|
struct passwd pwd;
|
||||||
|
struct passwd *ppwd;
|
||||||
|
|
||||||
|
uid = polkit_unix_user_get_uid (user);
|
||||||
|
if (getpwuid_r (uid, &pwd, buf, sizeof (buf), &ppwd) == 0)
|
||||||
|
{
|
||||||
|
if (!g_utf8_validate (pwd.pw_name, -1, NULL))
|
||||||
|
{
|
||||||
|
g_warning ("Invalid UTF-8 in username for uid %d. Skipping", uid);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_ptr_array_add (p, g_strdup (pwd.pw_name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_warning ("Error looking up user name for uid %d", uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_warning ("Unsupporting identity of GType %s", g_type_name (G_TYPE_FROM_INSTANCE (l->data)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_ptr_array_add (p, NULL);
|
||||||
|
user_names = (gchar **) g_ptr_array_free (p, FALSE);
|
||||||
|
g_signal_emit (request->agent,
|
||||||
|
signals[INITIATE_SIGNAL],
|
||||||
|
0, /* detail */
|
||||||
|
request->action_id,
|
||||||
|
request->message,
|
||||||
|
request->icon_name,
|
||||||
|
request->cookie,
|
||||||
|
user_names);
|
||||||
|
g_strfreev (user_names);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void auth_request_complete (AuthRequest *request);
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
handle_cancelled_in_idle (gpointer user_data)
|
||||||
|
{
|
||||||
|
AuthRequest *request = user_data;
|
||||||
|
|
||||||
|
print_debug ("CANCELLED %s cookie %s", request->action_id, request->cookie);
|
||||||
|
if (request == request->agent->current_request)
|
||||||
|
{
|
||||||
|
g_signal_emit (request->agent,
|
||||||
|
signals[CANCEL_SIGNAL],
|
||||||
|
0); /* detail */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auth_request_complete (request);
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_request_cancelled (GCancellable *cancellable,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
AuthRequest *request = user_data;
|
||||||
|
/* post-pone to idle to handle GCancellable deadlock in
|
||||||
|
*
|
||||||
|
* https://bugzilla.gnome.org/show_bug.cgi?id=642968
|
||||||
|
*/
|
||||||
|
g_idle_add (handle_cancelled_in_idle, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void maybe_process_next_request (ShellPolkitAuthenticationAgent *agent);
|
||||||
|
|
||||||
|
static void
|
||||||
|
auth_request_complete (AuthRequest *request)
|
||||||
|
{
|
||||||
|
ShellPolkitAuthenticationAgent *agent = request->agent;
|
||||||
|
|
||||||
|
if (agent->current_request == request)
|
||||||
|
{
|
||||||
|
print_debug ("COMPLETING CURRENT %s cookie %s", request->action_id, request->cookie);
|
||||||
|
|
||||||
|
g_simple_async_result_complete_in_idle (request->simple);
|
||||||
|
auth_request_free (request);
|
||||||
|
|
||||||
|
agent->current_request = NULL;
|
||||||
|
|
||||||
|
maybe_process_next_request (agent);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
print_debug ("COMPLETING SCHEDULED %s cookie %s", request->action_id, request->cookie);
|
||||||
|
agent->scheduled_requests = g_list_remove (agent->scheduled_requests, request);
|
||||||
|
g_simple_async_result_complete_in_idle (request->simple);
|
||||||
|
auth_request_free (request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
maybe_process_next_request (ShellPolkitAuthenticationAgent *agent)
|
||||||
|
{
|
||||||
|
print_debug ("MAYBE_PROCESS cur=%p len(scheduled)=%d", agent->current_request, g_list_length (agent->scheduled_requests));
|
||||||
|
|
||||||
|
if (agent->current_request == NULL && agent->scheduled_requests != NULL)
|
||||||
|
{
|
||||||
|
AuthRequest *request;
|
||||||
|
|
||||||
|
request = agent->scheduled_requests->data;
|
||||||
|
|
||||||
|
agent->current_request = request;
|
||||||
|
agent->scheduled_requests = g_list_remove (agent->scheduled_requests, request);
|
||||||
|
|
||||||
|
print_debug ("INITIATING %s cookie %s", request->action_id, request->cookie);
|
||||||
|
auth_request_initiate (request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
initiate_authentication (PolkitAgentListener *listener,
|
||||||
|
const gchar *action_id,
|
||||||
|
const gchar *message,
|
||||||
|
const gchar *icon_name,
|
||||||
|
PolkitDetails *details,
|
||||||
|
const gchar *cookie,
|
||||||
|
GList *identities,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
ShellPolkitAuthenticationAgent *agent = SHELL_POLKIT_AUTHENTICATION_AGENT (listener);
|
||||||
|
AuthRequest *request;
|
||||||
|
|
||||||
|
request = g_new0 (AuthRequest, 1);
|
||||||
|
request->agent = agent;
|
||||||
|
request->action_id = g_strdup (action_id);
|
||||||
|
request->message = g_strdup (message);
|
||||||
|
request->icon_name = g_strdup (icon_name);
|
||||||
|
request->details = g_object_ref (details);
|
||||||
|
request->cookie = g_strdup (cookie);
|
||||||
|
request->identities = g_list_copy (identities);
|
||||||
|
g_list_foreach (request->identities, (GFunc) g_object_ref, NULL);
|
||||||
|
request->simple = g_simple_async_result_new (G_OBJECT (listener),
|
||||||
|
callback,
|
||||||
|
user_data,
|
||||||
|
initiate_authentication);
|
||||||
|
request->cancellable = cancellable;
|
||||||
|
request->handler_id = g_cancellable_connect (request->cancellable,
|
||||||
|
G_CALLBACK (on_request_cancelled),
|
||||||
|
request,
|
||||||
|
NULL); /* GDestroyNotify for request */
|
||||||
|
|
||||||
|
print_debug ("SCHEDULING %s cookie %s", request->action_id, request->cookie);
|
||||||
|
agent->scheduled_requests = g_list_append (agent->scheduled_requests, request);
|
||||||
|
|
||||||
|
maybe_process_next_request (agent);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
initiate_authentication_finish (PolkitAgentListener *listener,
|
||||||
|
GAsyncResult *res,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
|
||||||
|
if (g_simple_async_result_propagate_error (simple, error))
|
||||||
|
return FALSE;
|
||||||
|
else
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
shell_polkit_authentication_agent_complete (ShellPolkitAuthenticationAgent *agent)
|
||||||
|
{
|
||||||
|
g_return_if_fail (SHELL_IS_POLKIT_AUTHENTICATION_AGENT (agent));
|
||||||
|
g_return_if_fail (agent->current_request != NULL);
|
||||||
|
|
||||||
|
auth_request_complete (agent->current_request);
|
||||||
|
}
|
32
src/shell-polkit-authentication-agent.h
Normal file
32
src/shell-polkit-authentication-agent.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 20011 Red Hat, Inc.
|
||||||
|
*
|
||||||
|
* Author: David Zeuthen <davidz@redhat.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SHELL_POLKIT_AUTHENTICATION_AGENT_H__
|
||||||
|
#define __SHELL_POLKIT_AUTHENTICATION_AGENT_H__
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
typedef struct _ShellPolkitAuthenticationAgent ShellPolkitAuthenticationAgent;
|
||||||
|
typedef struct _ShellPolkitAuthenticationAgentClass ShellPolkitAuthenticationAgentClass;
|
||||||
|
|
||||||
|
#define SHELL_TYPE_POLKIT_AUTHENTICATION_AGENT (shell_polkit_authentication_agent_get_type ())
|
||||||
|
#define SHELL_POLKIT_AUTHENTICATION_AGENT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SHELL_TYPE_POLKIT_AUTHENTICATION_AGENT, ShellPolkitAuthenticationAgent))
|
||||||
|
#define SHELL_POLKIT_AUTHENTICATION_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_POLKIT_AUTHENTICATION_AGENT, ShellPolkitAuthenticationAgentClass))
|
||||||
|
#define SHELL_IS_POLKIT_AUTHENTICATION_AGENT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SHELL_TYPE_POLKIT_AUTHENTICATION_AGENT))
|
||||||
|
#define SHELL_IS_POLKIT_AUTHENTICATION_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_POLKIT_AUTHENTICATION_AGENT))
|
||||||
|
#define SHELL_POLKIT_AUTHENTICATION_AGENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_POLKIT_AUTHENTICATION_AGENT, ShellPolkitAuthenticationAgentClass))
|
||||||
|
|
||||||
|
GType shell_polkit_authentication_agent_get_type (void) G_GNUC_CONST;
|
||||||
|
ShellPolkitAuthenticationAgent *shell_polkit_authentication_agent_new (void);
|
||||||
|
void shell_polkit_authentication_agent_complete (ShellPolkitAuthenticationAgent *agent);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __SHELL_POLKIT_AUTHENTICATION_AGENT_H__ */
|
Loading…
Reference in New Issue
Block a user