Compare commits
	
		
			16 Commits
		
	
	
		
			3.36.3
			...
			wip/smartc
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | d139adc25f | ||
|   | 0d151f8d94 | ||
|   | e3fdc2858d | ||
|   | 05dcd8575c | ||
|   | 214440fde4 | ||
|   | e3621e13ac | ||
|   | df0aace025 | ||
|   | 023f4b31d9 | ||
|   | 16fba7bd5f | ||
|   | ebce362c05 | ||
|   | c4ca17d127 | ||
|   | 3455dd1e76 | ||
|   | 57abfab923 | ||
|   | 3a13e7484a | ||
|   | a04895383a | ||
|   | 43357a1b69 | 
| @@ -34,7 +34,9 @@ nobase_dist_js_DATA = 	\ | ||||
| 	misc/jsParse.js		\ | ||||
| 	misc/loginManager.js	\ | ||||
| 	misc/modemManager.js	\ | ||||
| 	misc/objectManager.js	\ | ||||
| 	misc/params.js		\ | ||||
| 	misc/smartcardManager.js \ | ||||
| 	misc/util.js		\ | ||||
| 	perf/core.js		\ | ||||
| 	ui/altTab.js		\ | ||||
|   | ||||
| @@ -24,11 +24,23 @@ const AuthPromptMode = { | ||||
|     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({ | ||||
|     Name: 'AuthPrompt', | ||||
|  | ||||
|     _init: function(gdmClient, mode) { | ||||
|         this.verifyingUser = false; | ||||
|         this.verificationStatus = AuthPromptStatus.NOT_VERIFYING; | ||||
|  | ||||
|         this._gdmClient = gdmClient; | ||||
|         this._mode = mode; | ||||
| @@ -48,6 +60,8 @@ const AuthPrompt = new Lang.Class({ | ||||
|         this._userVerifier.connect('reset', Lang.bind(this, this._onReset)); | ||||
|         this._userVerifier.connect('show-login-hint', Lang.bind(this, this._onShowLoginHint)); | ||||
|         this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._onHideLoginHint)); | ||||
|         this._userVerifier.connect('smartcard-status-changed', Lang.bind(this, this._onSmartcardStatusChanged)); | ||||
|         this.smartcardDetected = this._userVerifier.smartcardDetected; | ||||
|  | ||||
|         this.connect('next', Lang.bind(this, function() { | ||||
|                          this.updateSensitivity(false); | ||||
| @@ -123,6 +137,8 @@ const AuthPrompt = new Lang.Class({ | ||||
|  | ||||
|     _onDestroy: function() { | ||||
|         this._userVerifier.clear(); | ||||
|         this._userVerifier.disconnectAll(); | ||||
|         this._userVerifier = null; | ||||
|     }, | ||||
|  | ||||
|     _initButtons: function() { | ||||
| @@ -193,11 +209,6 @@ const AuthPrompt = new Lang.Class({ | ||||
|         this.setPasswordChar(passwordChar); | ||||
|         this.setQuestion(question); | ||||
|  | ||||
|         if (this.verifyingUser) | ||||
|             this.cancelButton.show(); | ||||
|         else | ||||
|             this.cancelButton.hide(); | ||||
|  | ||||
|         if (passwordChar) { | ||||
|             if (this._userVerifier.reauthenticating) | ||||
|                 this.nextButton.label = _("Unlock"); | ||||
| @@ -211,8 +222,23 @@ const AuthPrompt = new Lang.Class({ | ||||
|         this.emit('prompted'); | ||||
|     }, | ||||
|  | ||||
|     _onSmartcardStatusChanged: function() { | ||||
|         this.smartcardDetected = this._userVerifier.smartcardDetected; | ||||
|  | ||||
|         // Don't reset on smartcard insertion if we're already verifying | ||||
|         // and the smartcard is the main service | ||||
|         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, styleClass) { | ||||
|         this.setMessage(message, styleClass); | ||||
|         this.emit('prompted'); | ||||
|     }, | ||||
|  | ||||
|     _onVerificationFailed: function() { | ||||
| @@ -220,16 +246,18 @@ const AuthPrompt = new Lang.Class({ | ||||
|  | ||||
|         this.updateSensitivity(true); | ||||
|         this.setActorInDefaultButtonWell(null); | ||||
|         this.userVerified = false; | ||||
|         this.verificationStatus = AuthPromptStatus.VERIFICATION_FAILED; | ||||
|     }, | ||||
|  | ||||
|     _onVerificationComplete: function() { | ||||
|         this.userVerified = true; | ||||
|         this.verificationStatus = AuthPromptStatus.VERIFICATION_SUCCEEDED; | ||||
|     }, | ||||
|  | ||||
|     _onReset: function() { | ||||
|         if (!this.userVerified) | ||||
|         if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED) { | ||||
|             this.verificationStatus = AuthPromptStatus.NOT_VERIFYING; | ||||
|             this.reset(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onShowLoginHint: function(verifier, message) { | ||||
| @@ -401,8 +429,12 @@ const AuthPrompt = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     reset: function() { | ||||
|         this.verifyingUser = false; | ||||
|         this.userVerified = false; | ||||
|         let oldStatus = this.verificationStatus; | ||||
|         this.verificationStatus = AuthPromptStatus.NOT_VERIFYING; | ||||
|  | ||||
|         if (oldStatus == AuthPromptStatus.VERIFYING) | ||||
|             this._userVerifier.cancel(); | ||||
|  | ||||
|         this._queryingService = null; | ||||
|         this.clear(); | ||||
|         this._message.opacity = 0; | ||||
| @@ -410,7 +442,25 @@ const AuthPrompt = new Lang.Class({ | ||||
|         this.stopSpinning(); | ||||
|         this.setHint(null); | ||||
|  | ||||
|         this.emit('reset'); | ||||
|         if (oldStatus == AuthPromptStatus.VERIFICATION_FAILED) | ||||
|             this.emit('failed'); | ||||
|  | ||||
|         let beginRequestType; | ||||
|  | ||||
|         if (this._mode == AuthPromptMode.UNLOCK_ONLY) { | ||||
|             // The user is constant at the unlock screen | ||||
|             beginRequestType = BeginRequestType.PROVIDE_USERNAME; | ||||
|         } else if (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. | ||||
|             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) { | ||||
| @@ -432,7 +482,7 @@ const AuthPrompt = new Lang.Class({ | ||||
|             hold = new Batch.Hold(); | ||||
|  | ||||
|         this._userVerifier.begin(params.userName, hold); | ||||
|         this.verifyingUser = true; | ||||
|         this.verificationStatus = AuthPromptStatus.VERIFYING; | ||||
|     }, | ||||
|  | ||||
|     finish: function(onComplete) { | ||||
| @@ -449,10 +499,8 @@ const AuthPrompt = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     cancel: function() { | ||||
|         if (this.verifyingUser) | ||||
|             this._userVerifier.cancel(); | ||||
|  | ||||
|         this.reset(); | ||||
|         this.emit('cancelled'); | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(AuthPrompt.prototype); | ||||
|   | ||||
| @@ -451,7 +451,7 @@ const LoginDialog = new Lang.Class({ | ||||
|  | ||||
|         this._authPrompt = new AuthPrompt.AuthPrompt(gdmClient, AuthPrompt.AuthPromptMode.UNLOCK_OR_LOG_IN); | ||||
|         this._authPrompt.connect('prompted', Lang.bind(this, this._onPrompted)); | ||||
|         this._authPrompt.connect('reset', Lang.bind(this, this._reset)); | ||||
|         this._authPrompt.connect('reset', Lang.bind(this, this._onReset)); | ||||
|         this._authPrompt.hide(); | ||||
|  | ||||
|         this._authPrompt.actor.add_constraint(new Clutter.AlignConstraint({ source: this.actor, | ||||
| @@ -475,7 +475,12 @@ const LoginDialog = new Lang.Class({ | ||||
|                                                 x_align: St.Align.START, | ||||
|                                                 x_fill: true }); | ||||
|  | ||||
|         this._notListedButton.connect('clicked', Lang.bind(this, this._hideUserListAndLogIn)); | ||||
|         this._notListedButton.connect('clicked', | ||||
|                                       Lang.bind(this, function() { | ||||
|             this._authPrompt.cancelButton.show(); | ||||
|             this._hideUserListAskForUsernameAndBeginVerification(); | ||||
|         })); | ||||
|  | ||||
|         this._notListedButton.hide(); | ||||
|  | ||||
|         this._userSelectionBox.add(this._notListedButton, | ||||
| @@ -529,8 +534,8 @@ const LoginDialog = new Lang.Class({ | ||||
|         if (disableUserList != this._disableUserList) { | ||||
|             this._disableUserList = disableUserList; | ||||
|  | ||||
|             if (!this._authPrompt.verifyingUser) | ||||
|                 this._reset(); | ||||
|             if (this._authPrompt.verificationStatus == AuthPrompt.AuthPromptStatus.NOT_VERIFYING) | ||||
|                 this._authPrompt.reset(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
| @@ -569,24 +574,25 @@ const LoginDialog = new Lang.Class({ | ||||
|  | ||||
|         if (this._shouldShowSessionMenuButton()) | ||||
|             this._authPrompt.setActorInDefaultButtonWell(this._sessionMenuButton.actor); | ||||
|  | ||||
|         this._authPrompt.cancelButton.show(); | ||||
|  | ||||
|         this._showPrompt(); | ||||
|     }, | ||||
|  | ||||
|     _reset: function() { | ||||
|         if (this._authPrompt.verifyingUser) | ||||
|             return; | ||||
|  | ||||
|     _onReset: function(authPrompt, beginRequest) { | ||||
|         this._sessionMenuButton.updateSensitivity(true); | ||||
|  | ||||
|         this._user = null; | ||||
|  | ||||
|         if (this._disableUserList) | ||||
|             this._hideUserListAndLogIn(); | ||||
|         else | ||||
|             this._showUserList(); | ||||
|         if (beginRequest == AuthPrompt.BeginRequestType.PROVIDE_USERNAME) { | ||||
|             if (this._disableUserList) { | ||||
|                 this._authPrompt.cancelButton.hide(); | ||||
|                 this._hideUserListAskForUsernameAndBeginVerification(); | ||||
|             } else { | ||||
|                 this._showUserList(); | ||||
|             } | ||||
|         } else { | ||||
|             this._authPrompt.cancelButton.hide(); | ||||
|             this._hideUserListAndBeginVerification(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onDefaultSessionChanged: function(client, sessionId) { | ||||
| @@ -630,7 +636,7 @@ const LoginDialog = new Lang.Class({ | ||||
|         this._authPrompt.setHint(_("(e.g., user or %s)").format(hint)); | ||||
|     }, | ||||
|  | ||||
|     _askForUsernameAndLogIn: function() { | ||||
|     _askForUsernameAndBeginVerification: function() { | ||||
|         this._authPrompt.setPasswordChar(''); | ||||
|         this._authPrompt.setQuestion(_("Username: ")); | ||||
|  | ||||
| @@ -645,13 +651,13 @@ const LoginDialog = new Lang.Class({ | ||||
|                                                         this._authPrompt.updateSensitivity(false); | ||||
|                                                         let answer = this._authPrompt.getAnswer(); | ||||
|                                                         this._authPrompt.clear(); | ||||
|                                                         this._authPrompt.cancelButton.show(); | ||||
|                                                         this._authPrompt.startSpinning(); | ||||
|                                                         this._authPrompt.begin({ userName: answer }); | ||||
|  | ||||
|                                                         realmManager.disconnect(realmSignalId) | ||||
|                                                         realmManager.release(); | ||||
|                                                     })); | ||||
|         this._authPrompt.cancelButton.hide(); | ||||
|         this._showPrompt(); | ||||
|     }, | ||||
|  | ||||
| @@ -813,16 +819,26 @@ const LoginDialog = new Lang.Class({ | ||||
|         this._userSelectionBox.visible = expanded; | ||||
|     }, | ||||
|  | ||||
|     _hideUserListAndLogIn: function() { | ||||
|     _hideUserList: function() { | ||||
|         this._setUserListExpanded(false); | ||||
|         if (this._userSelectionBox.visible) | ||||
|             GdmUtil.cloneAndFadeOutActor(this._userSelectionBox); | ||||
|         this._askForUsernameAndLogIn(); | ||||
|     }, | ||||
|  | ||||
|     _hideUserListAskForUsernameAndBeginVerification: function() { | ||||
|         this._hideUserList(); | ||||
|         this._askForUsernameAndBeginVerification(); | ||||
|     }, | ||||
|  | ||||
|     _hideUserListAndBeginVerification: function() { | ||||
|         this._hideUserList(); | ||||
|         this._authPrompt.begin(); | ||||
|     }, | ||||
|  | ||||
|     _showUserList: function() { | ||||
|         this._authPrompt.hide(); | ||||
|         this._sessionMenuButton.close(); | ||||
|         this._authPrompt.cancelButton.show(); | ||||
|         this._setUserListExpanded(true); | ||||
|         this._notListedButton.show(); | ||||
|         this._userList.actor.grab_key_focus(); | ||||
|   | ||||
							
								
								
									
										108
									
								
								js/gdm/util.js
									
									
									
									
									
								
							
							
						
						
									
										108
									
								
								js/gdm/util.js
									
									
									
									
									
								
							| @@ -13,15 +13,19 @@ const Fprint = imports.gdm.fingerprint; | ||||
| const Main = imports.ui.main; | ||||
| const Params = imports.misc.params; | ||||
| const ShellEntry = imports.ui.shellEntry; | ||||
| const SmartcardManager = imports.misc.smartcardManager; | ||||
| const Tweener = imports.ui.tweener; | ||||
|  | ||||
| const PASSWORD_SERVICE_NAME = 'gdm-password'; | ||||
| const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint'; | ||||
| const SMARTCARD_SERVICE_NAME = 'gdm-smartcard'; | ||||
| const FADE_ANIMATION_TIME = 0.16; | ||||
| const CLONE_FADE_ANIMATION_TIME = 0.25; | ||||
|  | ||||
| const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen'; | ||||
| const PASSWORD_AUTHENTICATION_KEY = 'enable-password-authentication'; | ||||
| const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication'; | ||||
| const SMARTCARD_AUTHENTICATION_KEY = 'enable-smartcard-authentication'; | ||||
| const BANNER_MESSAGE_KEY = 'banner-message-enable'; | ||||
| const BANNER_MESSAGE_TEXT_KEY = 'banner-message-text'; | ||||
| const ALLOWED_FAILURES_KEY = 'allowed-failures'; | ||||
| @@ -116,8 +120,30 @@ const ShellUserVerifier = new Lang.Class({ | ||||
|         this._client = client; | ||||
|  | ||||
|         this._settings = new Gio.Settings({ schema: LOGIN_SCREEN_SCHEMA }); | ||||
|         this._settings.connect('changed', | ||||
|                                Lang.bind(this, function() { | ||||
|                                    this._updateDefaultService(); | ||||
|                                })); | ||||
|         this._updateDefaultService(); | ||||
|  | ||||
|         this._fprintManager = new Fprint.FprintManager(); | ||||
|         this._smartcardManager = SmartcardManager.getSmartcardManager(); | ||||
|  | ||||
|         // We check for smartcards right away, since an inserted smartcard | ||||
|         // at startup should result in immediately initiating authentication. | ||||
|         // This is different than fingeprint readers, where we only check them | ||||
|         // after a user has been picked. | ||||
|         this._checkForSmartcard(); | ||||
|  | ||||
|         this._smartcardManager.connect('smartcard-inserted', | ||||
|                                        Lang.bind(this, function() { | ||||
|                                            this._checkForSmartcard(); | ||||
|                                        })); | ||||
|         this._smartcardManager.connect('smartcard-removed', | ||||
|                                        Lang.bind(this, function() { | ||||
|                                            this._checkForSmartcard(); | ||||
|                                        })); | ||||
|  | ||||
|         this._messageQueue = []; | ||||
|         this._messageQueueTimeoutId = 0; | ||||
|         this.hasPendingMessages = false; | ||||
| @@ -148,8 +174,10 @@ const ShellUserVerifier = new Lang.Class({ | ||||
|         if (this._cancellable) | ||||
|             this._cancellable.cancel(); | ||||
|  | ||||
|         if (this._userVerifier) | ||||
|         if (this._userVerifier) { | ||||
|             this._userVerifier.call_cancel_sync(null); | ||||
|             this.clear(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     clear: function() { | ||||
| @@ -244,6 +272,28 @@ const ShellUserVerifier = new Lang.Class({ | ||||
|             })); | ||||
|     }, | ||||
|  | ||||
|     _checkForSmartcard: function() { | ||||
|         let smartcardDetected; | ||||
|  | ||||
|         if (!this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY)) | ||||
|             smartcardDetected = false; | ||||
|         else if (this.reauthenticating) | ||||
|             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'); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _reportInitError: function(where, error) { | ||||
|         logError(error, where); | ||||
|         this._hold.release(); | ||||
| @@ -300,11 +350,35 @@ const ShellUserVerifier = new Lang.Class({ | ||||
|         this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete)); | ||||
|     }, | ||||
|  | ||||
|     _getForegroundService: function() { | ||||
|         if (this._preemptingService) | ||||
|             return this._preemptingService; | ||||
|  | ||||
|         return this._defaultService; | ||||
|     }, | ||||
|  | ||||
|     serviceIsForeground: function(serviceName) { | ||||
|         return serviceName == this._getForegroundService(); | ||||
|     }, | ||||
|  | ||||
|     serviceIsDefault: function(serviceName) { | ||||
|         return serviceName == this._defaultService; | ||||
|     }, | ||||
|  | ||||
|     _updateDefaultService: function() { | ||||
|         if (this._settings.get_boolean(PASSWORD_AUTHENTICATION_KEY)) | ||||
|             this._defaultService = PASSWORD_SERVICE_NAME; | ||||
|         else if (this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY)) | ||||
|             this._defaultService = SMARTCARD_SERVICE_NAME; | ||||
|         else if (this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY)) | ||||
|             this._defaultService = FINGERPRINT_SERVICE_NAME; | ||||
|     }, | ||||
|  | ||||
|     _beginVerification: function() { | ||||
|         this._hold.acquire(); | ||||
|  | ||||
|         if (this._userName) { | ||||
|             this._userVerifier.call_begin_verification_for_user(PASSWORD_SERVICE_NAME, | ||||
|             this._userVerifier.call_begin_verification_for_user(this._getForegroundService(), | ||||
|                                                                 this._userName, | ||||
|                                                                 this._cancellable, | ||||
|                                                                 Lang.bind(this, function(obj, result) { | ||||
| @@ -320,7 +394,7 @@ const ShellUserVerifier = new Lang.Class({ | ||||
|                 this._hold.release(); | ||||
|             })); | ||||
|  | ||||
|             if (this._haveFingerprintReader) { | ||||
|             if (this._haveFingerprintReader && !this.serviceIsForeground(FINGERPRINT_SERVICE_NAME)) { | ||||
|                 this._hold.acquire(); | ||||
|  | ||||
|                 this._userVerifier.call_begin_verification_for_user(FINGERPRINT_SERVICE_NAME, | ||||
| @@ -340,7 +414,7 @@ const ShellUserVerifier = new Lang.Class({ | ||||
|                 })); | ||||
|             } | ||||
|         } else { | ||||
|             this._userVerifier.call_begin_verification(PASSWORD_SERVICE_NAME, | ||||
|             this._userVerifier.call_begin_verification(this._getForegroundService(), | ||||
|                                                        this._cancellable, | ||||
|                                                        Lang.bind(this, function(obj, result) { | ||||
|                 try { | ||||
| @@ -358,39 +432,36 @@ const ShellUserVerifier = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _onInfo: function(client, serviceName, info) { | ||||
|         // We don't display fingerprint messages, because they | ||||
|         // have words like UPEK in them. Instead we use the messages | ||||
|         // as a cue to display our own message. | ||||
|         if (serviceName == FINGERPRINT_SERVICE_NAME && | ||||
|         if (this.serviceIsForeground(serviceName)) { | ||||
|             this._queueMessage(info, 'login-dialog-message-info'); | ||||
|         } else if (serviceName == FINGERPRINT_SERVICE_NAME && | ||||
|             this._haveFingerprintReader) { | ||||
|             // We don't show fingeprint messages directly since it's | ||||
|             // not the main auth service. Instead we use the messages | ||||
|             // as a cue to display our own message. | ||||
|  | ||||
|             // Translators: this message is shown below the password entry field | ||||
|             // to indicate the user can swipe their finger instead | ||||
|             this.emit('show-login-hint', _("(or swipe finger)")); | ||||
|         } else if (serviceName == PASSWORD_SERVICE_NAME) { | ||||
|             this._queueMessage(info, 'login-dialog-message-info'); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onProblem: function(client, serviceName, problem) { | ||||
|         // we don't want to show auth failed messages to | ||||
|         // users who haven't enrolled their fingerprint. | ||||
|         if (serviceName != PASSWORD_SERVICE_NAME) | ||||
|         if (!this.serviceIsForeground(serviceName)) | ||||
|             return; | ||||
|  | ||||
|         this._queueMessage(problem, 'login-dialog-message-warning'); | ||||
|     }, | ||||
|  | ||||
|     _onInfoQuery: function(client, serviceName, question) { | ||||
|         // We only expect questions to come from the main auth service | ||||
|         if (serviceName != PASSWORD_SERVICE_NAME) | ||||
|         if (!this.serviceIsForeground(serviceName)) | ||||
|             return; | ||||
|  | ||||
|         this.emit('ask-question', serviceName, question, ''); | ||||
|     }, | ||||
|  | ||||
|     _onSecretInfoQuery: function(client, serviceName, secretQuestion) { | ||||
|         // We only expect secret requests to come from the main auth service | ||||
|         if (serviceName != PASSWORD_SERVICE_NAME) | ||||
|         if (!this.serviceIsForeground(serviceName)) | ||||
|             return; | ||||
|  | ||||
|         this.emit('ask-question', serviceName, secretQuestion, '\u25cf'); | ||||
| @@ -399,6 +470,7 @@ const ShellUserVerifier = new Lang.Class({ | ||||
|     _onReset: function() { | ||||
|         // Clear previous attempts to authenticate | ||||
|         this._failCounter = 0; | ||||
|         this._updateDefaultService(); | ||||
|  | ||||
|         this.emit('reset'); | ||||
|     }, | ||||
| @@ -455,7 +527,7 @@ const ShellUserVerifier = new Lang.Class({ | ||||
|         // if the password service fails, then cancel everything. | ||||
|         // But if, e.g., fingerprint fails, still give | ||||
|         // password authentication a chance to succeed | ||||
|         if (serviceName == PASSWORD_SERVICE_NAME) { | ||||
|         if (this.serviceIsForeground(serviceName)) { | ||||
|             this._verificationFailed(true); | ||||
|         } | ||||
|  | ||||
|   | ||||
							
								
								
									
										275
									
								
								js/misc/objectManager.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										275
									
								
								js/misc/objectManager.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,275 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Lang = imports.lang; | ||||
| const Params = imports.misc.params; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| // Specified in the D-Bus specification here: | ||||
| // http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager | ||||
| const ObjectManagerIface = <interface name="org.freedesktop.DBus.ObjectManager"> | ||||
|   <method name="GetManagedObjects"> | ||||
|     <arg name="objects" type="a{oa{sa{sv}}}" direction="out"/> | ||||
|   </method> | ||||
|   <signal name="InterfacesAdded"> | ||||
|     <arg name="objectPath" type="o"/> | ||||
|     <arg name="interfaces" type="a{sa{sv}}" /> | ||||
|   </signal> | ||||
|   <signal name="InterfacesRemoved"> | ||||
|     <arg name="objectPath" type="o"/> | ||||
|     <arg name="interfaces" type="as" /> | ||||
|   </signal> | ||||
| </interface> | ||||
|  | ||||
| const ObjectManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(ObjectManagerIface); | ||||
|  | ||||
| const ObjectManager = new Lang.Class({ | ||||
|     Name: 'ObjectManager', | ||||
|     _init: function(params) { | ||||
|         params = Params.parse(params, { connection: null, | ||||
|                                         name: null, | ||||
|                                         objectPath: null, | ||||
|                                         knownInterfaces: null, | ||||
|                                         cancellable: null, | ||||
|                                         onLoaded: null }); | ||||
|  | ||||
|         this._connection = params.connection; | ||||
|         this._serviceName = params.name; | ||||
|         this._managerPath = params.objectPath; | ||||
|         this._cancellable = params.cancellable; | ||||
|  | ||||
|         this._managerProxy = new Gio.DBusProxy({ g_connection: this._connection, | ||||
|                                                  g_interface_name: ObjectManagerInfo.name, | ||||
|                                                  g_interface_info: ObjectManagerInfo, | ||||
|                                                  g_name: this._serviceName, | ||||
|                                                  g_object_path: this._managerPath, | ||||
|                                                  g_flags: Gio.DBusProxyFlags.NONE }); | ||||
|  | ||||
|         this._interfaceInfos = {}; | ||||
|         this._objects = {}; | ||||
|         this._interfaces = {}; | ||||
|         this._pendingProxies = []; | ||||
|         this._onLoaded = params.onLoaded; | ||||
|  | ||||
|         if (params.knownInterfaces) | ||||
|             this._registerInterfaces(params.knownInterfaces); | ||||
|  | ||||
|         this._managerProxy.init_async(GLib.PRIORITY_DEFAULT, | ||||
|                                       this._cancellable, | ||||
|                                       Lang.bind(this, this._onManagerProxyLoaded)); | ||||
|     }, | ||||
|  | ||||
|     _addInterface: function(objectPath, interfaceName, onFinished) { | ||||
|         let info = this._interfaceInfos[interfaceName]; | ||||
|  | ||||
|         if (!info) | ||||
|             return; | ||||
|  | ||||
|         let proxy = new Gio.DBusProxy({ g_connection: this._connection, | ||||
|                                         g_name: this._serviceName, | ||||
|                                         g_object_path: objectPath, | ||||
|                                         g_interface_name: interfaceName, | ||||
|                                         g_interface_info: info, | ||||
|                                         g_flags: Gio.DBusProxyFlags.NONE }); | ||||
|  | ||||
|         this._pendingProxies.push(proxy); | ||||
|  | ||||
|         proxy.init_async(GLib.PRIORITY_DEFAULT, | ||||
|                          this._cancellable, | ||||
|                          Lang.bind(this, function(initable, result) { | ||||
|                              let index = this._pendingProxies.indexOf(proxy); | ||||
|  | ||||
|                              if (index >= 0) | ||||
|                                  this._pendingProxies.splice(index, 1); | ||||
|  | ||||
|                              let error = null; | ||||
|                              try { | ||||
|                                  initable.init_finish(result); | ||||
|                              } catch(e) { | ||||
|                                  logError(e, 'could not initialize proxy for interface ' + interfaceName); | ||||
|  | ||||
|                                  if (onFinished) | ||||
|                                      onFinished(); | ||||
|                                  return; | ||||
|                              } | ||||
|  | ||||
|                              let isNewObject; | ||||
|  | ||||
|                              if (!this._objects[objectPath]) { | ||||
|                                  this._objects[objectPath] = {}; | ||||
|                                  isNewObject = true; | ||||
|                              } else { | ||||
|                                  isNewObject = false; | ||||
|                              } | ||||
|  | ||||
|                              this._objects[objectPath][interfaceName] = proxy; | ||||
|  | ||||
|                              if (!this._interfaces[interfaceName]) | ||||
|                                  this._interfaces[interfaceName] = []; | ||||
|  | ||||
|                              this._interfaces[interfaceName].push(proxy); | ||||
|  | ||||
|                              if (isNewObject) | ||||
|                                  this.emit('object-added', objectPath); | ||||
|  | ||||
|                              this.emit('interface-added', interfaceName, proxy); | ||||
|  | ||||
|                              if (onFinished) | ||||
|                                  onFinished(); | ||||
|                          })); | ||||
|     }, | ||||
|  | ||||
|     _removeInterface: function(objectPath, interfaceName) { | ||||
|         if (!this._objects[objectPath]) | ||||
|             return; | ||||
|  | ||||
|         let proxy = this._objects[objectPath][interfaceName]; | ||||
|  | ||||
|         if (this._interfaces[interfaceName]) { | ||||
|             let index = this._interfaces[interfaceName].indexOf(proxy); | ||||
|  | ||||
|             if (index >= 0) | ||||
|                 this._interfaces[interfaceName].splice(index, 1); | ||||
|  | ||||
|             if (this._interfaces[interfaceName].length == 0) | ||||
|                 delete this._interfaces[interfaceName]; | ||||
|         } | ||||
|  | ||||
|         this.emit('interface-removed', interfaceName, proxy); | ||||
|  | ||||
|         this._objects[objectPath][interfaceName] = null; | ||||
|  | ||||
|         if (Object.keys(this._objects[objectPath]).length == 0) { | ||||
|             delete this._objects[objectPath]; | ||||
|             this.emit('object-removed', objectPath); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onManagerProxyLoaded: function(initable, result) { | ||||
|         let error = null; | ||||
|         try { | ||||
|             initable.init_finish(result); | ||||
|         } catch(e) { | ||||
|             logError(e, 'could not initialize object manager for object ' + params.name); | ||||
|  | ||||
|             if (this._onLoaded) | ||||
|                 this._onLoaded(); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         this._managerProxy.connectSignal('InterfacesAdded', | ||||
|                                          Lang.bind(this, function(objectManager, sender, [objectPath, interfaces]) { | ||||
|                                              let interfaceNames = Object.keys(interfaces); | ||||
|                                              for (let i = 0; i < interfaceNames.length; i++) | ||||
|                                                  this._addInterface(objectPath, interfaceNames[i]); | ||||
|                                          })); | ||||
|         this._managerProxy.connectSignal('InterfacesRemoved', | ||||
|                                          Lang.bind(this, function(objectManager, sender, [objectPath, interfaceNames]) { | ||||
|                                              for (let i = 0; i < interfaceNames.length; i++) | ||||
|                                                  this._removeInterface(objectPath, interfaceNames[i]); | ||||
|                                          })); | ||||
|  | ||||
|  | ||||
|         this._managerProxy.GetManagedObjectsRemote(Lang.bind(this, function(result, error) { | ||||
|             if (!result) { | ||||
|                 if (error) { | ||||
|                    logError(error, 'could not get remote objects for service ' + this._serviceName + ' path ' + this._managerPath); | ||||
|                 } | ||||
|  | ||||
|                 if (this._onLoaded) | ||||
|                     this._onLoaded(); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             let [objects] = result; | ||||
|  | ||||
|             if (Object.keys(this._interfaceInfos).length == 0) { | ||||
|                 if (this._onLoaded) | ||||
|                     this._onLoaded(); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             let numLoadInhibitors = 0; | ||||
|  | ||||
|             // First inhibitor is to prevent onLoaded from getting | ||||
|             // called until all interfaces have started being added. | ||||
|             // Subsequent inhibitors are to prevent onLoaded from getting | ||||
|             // called until all interfaces finish getting added. | ||||
|             numLoadInhibitors++; | ||||
|             let objectPaths = Object.keys(objects); | ||||
|             for (let i = 0; i < objectPaths.length; i++) { | ||||
|                 let objectPath = objectPaths[i]; | ||||
|                 let object = objects[objectPath]; | ||||
|  | ||||
|                 let interfaceNames = Object.keys(object); | ||||
|                 for (let j = 0; j < interfaceNames.length; j++) { | ||||
|                     let interfaceName = interfaceNames[j]; | ||||
|  | ||||
|                     numLoadInhibitors++; | ||||
|                     this._addInterface(objectPath, | ||||
|                                        interfaceName, | ||||
|                                        Lang.bind(this, function() { | ||||
|                                            numLoadInhibitors--; | ||||
|  | ||||
|                                            if (numLoadInhibitors == 0) { | ||||
|                                                if (this._onLoaded) | ||||
|                                                    this._onLoaded(); | ||||
|                                            } | ||||
|                                        })); | ||||
|                 } | ||||
|             } | ||||
|             numLoadInhibitors--; | ||||
|  | ||||
|             if (numLoadInhibitors == 0) { | ||||
|                 if (this._onLoaded) | ||||
|                     this._onLoaded(); | ||||
|             } | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     _registerInterfaces: function(interfaces) { | ||||
|         for (let i = 0; i < interfaces.length; i++) { | ||||
|             let info = Gio.DBusInterfaceInfo.new_for_xml(interfaces[i]); | ||||
|  | ||||
|             this._interfaceInfos[info.name] = info; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     getProxy: function(objectPath, interfaceName) { | ||||
|         let object = this._objects[objectPath]; | ||||
|  | ||||
|         if (!object) | ||||
|             return null; | ||||
|  | ||||
|         return object[interfaceName]; | ||||
|     }, | ||||
|  | ||||
|     getProxiesForInterface: function(interfaceName) { | ||||
|         let proxyList = this._interfaces[interfaceName]; | ||||
|  | ||||
|         if (!proxyList) | ||||
|             return []; | ||||
|  | ||||
|         return proxyList; | ||||
|     }, | ||||
|  | ||||
|     getAllProxies: function() { | ||||
|         let proxies = []; | ||||
|  | ||||
|         let objectPaths = Object.keys(this._objects); | ||||
|         for (let i = 0; i < objectPaths.length; i++) { | ||||
|             let object = this._objects[objectPaths]; | ||||
|  | ||||
|             let interfaceNames = Object.keys(object); | ||||
|             for (let j = 0; i < interfaceNames.length; i++) { | ||||
|                 let interfaceName = interfaceNames[i]; | ||||
|                 if (object[interfaceName]) | ||||
|                     proxies.push(object(interfaceName)); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return proxies; | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(ObjectManager.prototype); | ||||
							
								
								
									
										142
									
								
								js/misc/smartcardManager.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								js/misc/smartcardManager.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,142 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Gio = imports.gi.Gio; | ||||
| const Lang = imports.lang; | ||||
| const Shell = imports.gi.Shell; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const ObjectManager = imports.misc.objectManager; | ||||
|  | ||||
| const _SMARTCARD_SERVICE_DBUS_NAME = "org.gnome.SettingsDaemon.Smartcard"; | ||||
|  | ||||
| const SmartcardManagerIface = <interface name="org.gnome.SettingsDaemon.Smartcard.Manager"> | ||||
|   <method name="GetLoginToken"> | ||||
|       <arg name="token" type="o" direction="out"/> | ||||
|   </method> | ||||
|   <method name="GetInsertedTokens"> | ||||
|       <arg name="tokens" type="ao" direction="out"/> | ||||
|   </method> | ||||
| </interface>; | ||||
|  | ||||
| const SmartcardTokenIface = <interface name="org.gnome.SettingsDaemon.Smartcard.Token"> | ||||
|   <property name="Name" type="s" access="read"/> | ||||
|   <property name="Driver" type="o" access="read"/> | ||||
|   <property name="IsInserted" type="b" access="read"/> | ||||
|   <property name="UsedToLogin" type="b" access="read"/> | ||||
| </interface>; | ||||
|  | ||||
| const SmartcardDriverIface = <interface name="org.gnome.SettingsDaemon.Smartcard.Driver"> | ||||
|   <property name="Library" type="s" access="read"/> | ||||
|   <property name="Description" type="s" access="read"/> | ||||
| </interface>; | ||||
|  | ||||
| let _smartcardManager = null; | ||||
|  | ||||
| function getSmartcardManager() { | ||||
|     if (_smartcardManager == null) | ||||
|         _smartcardManager = new SmartcardManager(); | ||||
|  | ||||
|     return _smartcardManager; | ||||
| } | ||||
|  | ||||
| const SmartcardManager = new Lang.Class({ | ||||
|     Name: 'SmartcardManager', | ||||
|     _init: function() { | ||||
|         this._objectManager = new ObjectManager.ObjectManager({ connection: Gio.DBus.session, | ||||
|                                                                 name: _SMARTCARD_SERVICE_DBUS_NAME, | ||||
|                                                                 objectPath: '/org/gnome/SettingsDaemon/Smartcard', | ||||
|                                                                 knownInterfaces: [ SmartcardManagerIface, | ||||
|                                                                                    SmartcardTokenIface, | ||||
|                                                                                    SmartcardDriverIface ], | ||||
|                                                                 onLoaded: Lang.bind(this, this._onLoaded) }); | ||||
|         this._insertedTokens = {}; | ||||
|         this._removedTokens = {}; | ||||
|         this._loginToken = null; | ||||
|     }, | ||||
|  | ||||
|     _onLoaded: function() { | ||||
|         let tokens = this._objectManager.getProxiesForInterface('org.gnome.SettingsDaemon.Smartcard.Token'); | ||||
|  | ||||
|         for (let i = 0; i < tokens.length; i++) | ||||
|             this._addToken(tokens[i]); | ||||
|  | ||||
|         this._objectManager.connect('interface-added', Lang.bind(this, function(objectManager, interfaceName, proxy) { | ||||
|                                         if (interfaceName == 'org.gnome.SettingsDaemon.Smartcard.Token') | ||||
|                                             this._addToken(proxy); | ||||
|                                     })); | ||||
|  | ||||
|         this._objectManager.connect('interface-removed', Lang.bind(this, function(objectManager, interfaceName, proxy) { | ||||
|                                         if (interfaceName == 'org.gnome.SettingsDaemon.Smartcard.Token') | ||||
|                                             this._removeToken(proxy); | ||||
|                                     })); | ||||
|     }, | ||||
|  | ||||
|     _updateToken: function(token) { | ||||
|         let objectPath = token.get_object_path(); | ||||
|  | ||||
|         delete this._insertedTokens[objectPath]; | ||||
|         delete this._removedTokens[objectPath]; | ||||
|  | ||||
|         if (token.IsInserted) | ||||
|             this._insertedTokens[objectPath] = token; | ||||
|         else | ||||
|             this._removedTokens[objectPath] = token; | ||||
|  | ||||
|         if (token.UsedToLogin) | ||||
|             this._loginToken = token; | ||||
|     }, | ||||
|  | ||||
|     _addToken: function(token) { | ||||
|         this._updateToken(token); | ||||
|  | ||||
|         token.connect('g-properties-changed', | ||||
|                       Lang.bind(this, function(proxy, properties) { | ||||
|                           if ('IsInserted' in properties.deep_unpack()) { | ||||
|                               this._updateToken(token); | ||||
|  | ||||
|                               if (token.IsInserted) { | ||||
|                                   this.emit('smartcard-inserted', token.Name); | ||||
|                               } else { | ||||
|                                   this.emit('smartcard-removed', token.Name); | ||||
|                               } | ||||
|                           } | ||||
|                       })); | ||||
|  | ||||
|         // Emit a smartcard-inserted at startup if it's already plugged in | ||||
|         if (token.IsInserted) | ||||
|             this.emit('smartcard-inserted', token.Name); | ||||
|     }, | ||||
|  | ||||
|     _removeToken: function(token) { | ||||
|         let objectPath = token.get_object_path(); | ||||
|  | ||||
|         if (objectPath) { | ||||
|             if (this._removedTokens[objectPath] == token) { | ||||
|                 delete this._removedTokens[objectPath]; | ||||
|             } | ||||
|  | ||||
|             if (this._insertedTokens[objectPath] == token) { | ||||
|                 delete this._insertedTokens[objectPath]; | ||||
|                 this.emit('smartcard-removed', token.Name); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         token.disconnectAll(); | ||||
|     }, | ||||
|  | ||||
|     hasInsertedTokens: function() { | ||||
|         return Object.keys(this._insertedTokens).length > 0; | ||||
|     }, | ||||
|  | ||||
|     hasInsertedLoginToken: function() { | ||||
|         if (!this._loginToken) | ||||
|             return false; | ||||
|  | ||||
|         if (!this._loginToken.IsInserted) | ||||
|             return false; | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
| }); | ||||
| Signals.addSignalMethods(SmartcardManager.prototype); | ||||
| @@ -23,6 +23,7 @@ const Main = imports.ui.main; | ||||
| const Overview = imports.ui.overview; | ||||
| const MessageTray = imports.ui.messageTray; | ||||
| const ShellDBus = imports.ui.shellDBus; | ||||
| const SmartcardManager = imports.misc.smartcardManager; | ||||
| const Tweener = imports.ui.tweener; | ||||
| const Util = imports.misc.util; | ||||
|  | ||||
| @@ -516,6 +517,13 @@ const ScreenShield = new Lang.Class({ | ||||
|  | ||||
|         this._screenSaverDBus = new ShellDBus.ScreenSaverDBus(this); | ||||
|  | ||||
|         this._smartcardManager = SmartcardManager.getSmartcardManager(); | ||||
|         this._smartcardManager.connect('smartcard-inserted', | ||||
|                                        Lang.bind(this, function() { | ||||
|                                            if (this._isLocked) | ||||
|                                                this._liftShield(true, 0); | ||||
|                                        })); | ||||
|  | ||||
|         this._inhibitor = null; | ||||
|         this._aboutToSuspend = false; | ||||
|         this._loginManager = LoginManager.getLoginManager(); | ||||
|   | ||||
| @@ -49,8 +49,9 @@ const UnlockDialog = new Lang.Class({ | ||||
|                                                                      factor: 0.5 })); | ||||
|  | ||||
|         this._authPrompt = new AuthPrompt.AuthPrompt(new Gdm.Client(), AuthPrompt.AuthPromptMode.UNLOCK_ONLY); | ||||
|         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.setUser(this._user); | ||||
|         this._authPrompt.setPasswordChar('\u25cf'); | ||||
|         this._authPrompt.nextButton.label = _("Unlock"); | ||||
|  | ||||
| @@ -74,7 +75,7 @@ const UnlockDialog = new Lang.Class({ | ||||
|             this._otherUserButton = null; | ||||
|         } | ||||
|  | ||||
|         this._authPrompt.begin({ userName: this._userName }); | ||||
|         this._authPrompt.reset(); | ||||
|         this._updateSensitivity(true); | ||||
|  | ||||
|         Main.ctrlAltTabManager.addGroup(this.actor, _("Unlock Window"), 'dialog-password-symbolic'); | ||||
| @@ -92,15 +93,25 @@ const UnlockDialog = new Lang.Class({ | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onReset: function() { | ||||
|     _fail: function() { | ||||
|         this.emit('failed'); | ||||
|     }, | ||||
|  | ||||
|     _escape: function() { | ||||
|         if (this.allowCancel) { | ||||
|             this._authPrompt.cancel(); | ||||
|             this.emit('failed'); | ||||
|     _onReset: function(authPrompt, beginRequest) { | ||||
|         let userName; | ||||
|         if (beginRequest == AuthPrompt.BeginRequestType.PROVIDE_USERNAME) { | ||||
|             this._authPrompt.setUser(this._user); | ||||
|             userName = this._userName; | ||||
|         } else { | ||||
|             userName = null; | ||||
|         } | ||||
|  | ||||
|         this._authPrompt.begin({ userName: userName }); | ||||
|     }, | ||||
|  | ||||
|     _escape: function() { | ||||
|         if (this.allowCancel) | ||||
|             this._authPrompt.cancel(); | ||||
|     }, | ||||
|  | ||||
|     _otherUserClicked: function(button, event) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user