105
81
class SSOWizardPage(QWizardPage):
106
82
"""Root class for all wizard pages."""
108
def __init__(self, ui, controller, parent=None):
84
def __init__(self, ui, app_name=None, title='', subtitle='', parent=None):
109
85
"""Create a new instance."""
110
86
super(SSOWizardPage, self).__init__(parent)
112
88
self.ui.setupUi(self)
89
self.overlay = LoadingOverlay(self)
91
self.app_name = app_name
113
92
self.header = Header()
93
self.header.set_title(title)
94
self.header.set_subtitle(subtitle)
114
95
self.layout().insertWidget(0, self.header)
115
self.controller = controller
117
self.controller.setupUi(self)
96
self.message_box = QMessageBox
99
self._signals_receivers = {}
102
@defer.inlineCallbacks
103
def get_backend(self):
104
"""Return the backend used by the controller."""
105
if self.backend is None:
106
client = yield main.get_sso_client()
107
self.backend = client.sso_login
108
defer.returnValue(self.backend)
120
110
# pylint: disable=C0103
121
111
def nextId(self):
122
112
"""Provide the next id."""
124
# pylint: enable=C0103
126
# pylint: disable=C0103
127
def initializePage(self):
128
"""Called to prepare the page just before it is shown."""
130
self.controller.pageInitialized()
131
# pylint: enable=C0103
133
# pylint: disable=C0103
115
def resizeEvent(self, event):
116
"""Resize the overlay to fit all the widget."""
117
QWizardPage.resizeEvent(self, event)
118
self.overlay.resize(event.size())
134
120
def setTitle(self, title=''):
135
121
"""Set the Wizard Page Title."""
136
122
self.header.set_title(title)
137
# pylint: enable=C0103
139
# pylint: disable=C0103
140
124
def setSubTitle(self, subtitle=''):
141
125
"""Set the Wizard Page Subtitle."""
142
126
self.header.set_subtitle(subtitle)
143
127
# pylint: enable=C0103
129
def _filter_by_app_name(self, f):
130
"""Excecute the decorated function only for 'self.app_name'."""
133
def inner(app_name, *args, **kwargs):
134
"""Execute 'f' only if 'app_name' matches 'self.app_name'."""
136
if app_name == self.app_name:
137
result = f(app_name, *args, **kwargs)
139
logger.info('%s: ignoring call since received app_name '\
140
'"%s" (expected "%s")',
141
f.__name__, app_name, self.app_name)
146
def _setup_signals(self):
147
"""Bind signals to callbacks to be able to test the pages."""
148
for signal, method in self._signals.iteritems():
149
actual = self._signals_receivers.get(signal)
150
if actual is not None:
151
msg = 'Signal %r is already connected with %r.'
152
logger.warning(msg, signal, actual)
154
match = self.backend.connect_to_signal(signal, method)
155
self._signals_receivers[signal] = match
146
158
class EnhancedLineEdit(object):
147
159
"""Represents and enhanced lineedit.
199
212
# create a new enhanced edit
200
213
enhanced_edit = EnhancedLineEdit(edit, cb)
201
214
self._enhanced_edits[edit] = enhanced_edit
204
class ChooseSignInPage(SSOWizardPage):
205
"""Widget that allows the user to choose how to sign in."""
208
class CurrentUserSignInPage(SSOWizardPage):
209
"""Widget that allows to get the data of user to sign in."""
212
class EmailVerificationPage(SSOWizardPage):
213
"""Widget used to input the email verification code."""
216
def verification_code(self):
217
"""Return the content of the verification code edit."""
218
return str(self.ui.verification_code_edit.text())
221
def next_button(self):
222
"""Return the button that move to the next stage."""
223
return self.ui.next_button
226
class ErrorPage(SSOWizardPage):
227
"""Widget used to show the diff errors."""
230
class ForgottenPasswordPage(SSOWizardEnhancedEditPage):
231
"""Widget used to deal with users that forgot the password."""
234
def email_widget(self):
235
"""Return the widget used to show the email information."""
236
return self.ui.email_widget
239
def forgotted_password_intro_label(self):
240
"""Return the intro label that lets the user know the issue."""
241
return self.ui.forgotted_password_intro_label
244
def error_label(self):
245
"""Return the label used to show error."""
246
return self.ui.error_label
249
def email_address_label(self):
250
"""Return the lable used to state the use of the line edit."""
251
return self.ui.email_address_label
254
def email_address(self):
255
"""Return the email address provided by the user."""
256
return str(self.ui.email_line_edit.text())
259
def email_address_line_edit(self):
260
"""Return the line edit with the content."""
261
return self.ui.email_line_edit
264
def send_button(self):
265
"""Return the button used to request the new password."""
266
return self.ui.send_button
269
def try_again_widget(self):
270
"""Return the widget used to display the try again button."""
271
return self.ui.try_again_widget
274
def try_again_button(self):
275
"""Return the button used to try again the reset password."""
276
return self.ui.try_again_button
279
class ResetPasswordPage(SSOWizardEnhancedEditPage):
280
"""Widget used to allow the user change his password."""
282
def __init__(self, ui, controller, parent=None):
283
"""Create a new instance."""
284
super(ResetPasswordPage, self).__init__(ui, controller, parent)
285
self.ui.password_line_edit.textEdited.connect(
286
lambda: common.password_assistance(self.ui.password_line_edit,
287
self.ui.password_assistance,
289
self.ui.confirm_password_line_edit.textEdited.connect(
290
lambda: common.password_check_match(self.ui.password_line_edit,
291
self.ui.confirm_password_line_edit,
292
self.ui.password_assistance))
294
def focus_changed(self, old, now):
295
"""Check who has the focus to activate password popups if necessary."""
296
if now == self.ui.password_line_edit:
297
self.ui.password_assistance.setVisible(True)
298
common.password_default_assistance(self.ui.password_assistance)
299
elif now == self.ui.confirm_password_line_edit:
300
common.password_check_match(self.ui.password_line_edit,
301
self.ui.confirm_password_line_edit,
302
self.ui.password_assistance)
304
# Invalid name "initializePage"
305
# pylint: disable=C0103
307
def initializePage(self):
308
super(ResetPasswordPage, self).initializePage()
309
common.password_default_assistance(self.ui.password_assistance)
310
self.ui.password_assistance.setVisible(False)
311
self.setTitle(RESET_TITLE)
312
self.setSubTitle(RESET_SUBTITLE)
313
self.ui.password_label.setText(PASSWORD1_ENTRY)
314
self.ui.confirm_password_label.setText(PASSWORD2_ENTRY)
315
self.ui.reset_code.setText(RESET_CODE_ENTRY)
317
def showEvent(self, event):
318
"""Connect focusChanged signal from the application."""
319
super(ResetPasswordPage, self).showEvent(event)
320
self.connect(QApplication.instance(),
321
SIGNAL("focusChanged(QWidget*, QWidget*)"),
324
def hideEvent(self, event):
325
"""Disconnect the focusChanged signal when the page change."""
326
super(ResetPasswordPage, self).hideEvent(event)
328
self.disconnect(QApplication.instance(),
329
SIGNAL("focusChanged(QWidget*, QWidget*)"),
334
# pylint: enable=C0103
337
class SetupAccountPage(SSOWizardEnhancedEditPage):
338
"""Widget used to create a new account."""
340
def __init__(self, ui, controller, parent=None):
341
"""Create a new widget to be used."""
342
super(SetupAccountPage, self).__init__(ui, controller, parent)
343
# palettes that will be used to set the colors of the password strengh
344
self.captcha_id = None
345
self.captcha_file = None
346
self.ui.captcha_view.setPixmap(QPixmap())
348
def get_captcha_image(self):
349
"""Return the path to the captcha image."""
350
return self.ui.captcha_view.pixmap()
352
def set_captcha_image(self, pixmap_image):
353
"""Set the new image of the captcha."""
354
# lets set the QPixmap for the label
355
self.ui.captcha_view.setPixmap(pixmap_image)
357
captcha_image = property(get_captcha_image, set_captcha_image)
360
class SuccessPage(SSOWizardPage):
361
"""Page used to display success message."""
364
class UbuntuSSOWizard(QWizard):
365
"""Wizard used to create or use sso."""
367
# definition of the signals raised by the widget
368
recoverableError = pyqtSignal('QString', 'QString')
369
loginSuccess = pyqtSignal('QString', 'QString')
370
registrationSuccess = pyqtSignal('QString', 'QString')
372
def __init__(self, controller, app_name, **kwargs):
373
"""Create a new wizard."""
374
parent = kwargs.get('parent')
375
super(UbuntuSSOWizard, self).__init__(parent)
377
# store common useful data provided by the app
378
self.app_name = app_name
379
self.ping_url = kwargs.get('ping_url', '')
380
self.tc_url = kwargs.get('tc_url', '')
381
self.help_text = kwargs.get('help_text', '')
382
self.login_only = kwargs.get('login_only', False)
383
self.close_callback = kwargs.get('close_callback', lambda: None)
385
# set the diff pages of the QWizard
386
self.sign_in_controller = ChooseSignInController(title='Sign In')
387
self.sign_in_page = ChooseSignInPage(Ui_ChooseSignInPage(),
388
self.sign_in_controller,
390
self.setup_controller = SetUpAccountController()
391
self.setup_account = SetupAccountPage(Ui_SetUpAccountPage(),
392
self.setup_controller,
394
self.email_verification = EmailVerificationPage(
395
Ui_EmailVerificationPage(),
396
EmailVerificationController())
397
self.current_user_controller = CurrentUserController(title='Sign in')
398
self.current_user = CurrentUserSignInPage(Ui_CurrentUserSignInPage(),
399
self.current_user_controller,
401
self.success_controller = SuccessController()
402
self.success = SuccessPage(Ui_SuccessPage(), self.success_controller,
404
self.error_controller = ErrorController()
405
self.error = ErrorPage(Ui_ErrorPage(), self.error_controller)
406
self.forgotte_pwd_controller = ForgottenPasswordController()
407
self.forgotten = ForgottenPasswordPage(Ui_ForgottenPasswordPage(),
408
self.forgotte_pwd_controller,
410
self.reset_password_controller = ResetPasswordController()
411
self.reset_password = ResetPasswordPage(Ui_ResetPasswordPage(),
412
self.reset_password_controller,
414
# store the ids of the pages so that it is easier to access them later
416
for page in [self.sign_in_page, self.setup_account,
417
self.email_verification, self.current_user, self.success,
418
self.error, self.forgotten, self.reset_password]:
419
self._pages[page] = self.addPage(page)
421
# set the buttons layout to only have cancel and back since the next
422
# buttons are the ones used in the diff pages.
424
buttons_layout.append(QWizard.Stretch)
425
buttons_layout.append(QWizard.BackButton)
426
buttons_layout.append(QWizard.CancelButton)
427
self.setButtonLayout(buttons_layout)
428
self.setWindowTitle(self.app_name)
429
self.controller = controller
430
self.controller.setupUi(self)
433
def sign_in_page_id(self):
434
"""Return the id of the page used for choosing sign in type."""
435
return self._pages[self.sign_in_page]
438
def setup_account_page_id(self):
439
"""Return the id of the page used for sign in."""
440
return self._pages[self.setup_account]
443
def email_verification_page_id(self):
444
"""Return the id of the verification page."""
445
return self._pages[self.email_verification]
448
def current_user_page_id(self):
449
"""Return the id used to signin by a current user."""
450
return self._pages[self.current_user]
453
def success_page_id(self):
454
"""Return the id of the success page."""
455
return self._pages[self.success]
458
def forgotten_password_page_id(self):
459
"""Return the id of the forgotten password page."""
460
return self._pages[self.forgotten]
463
def reset_password_page_id(self):
464
"""Return the id of the reset password page."""
465
return self._pages[self.reset_password]
468
def error_page_id(self):
469
"""Return the id of the error page."""
470
return self._pages[self.error]
473
class UbuntuSSOClientGUI(object):
474
"""Ubuntu single sign-on GUI."""
476
def __init__(self, app_name, **kwargs):
477
"""Create a new instance."""
478
super(UbuntuSSOClientGUI, self).__init__()
479
logger.debug('UbuntuSSOClientGUI: app_name %r, kwargs %r.',
481
self.app_name = app_name
482
# create the controller and the ui, then set the cb and call the show
483
# method so that we can work
484
self.controller = UbuntuSSOWizardController(app_name)
485
self.view = UbuntuSSOWizard(self.controller, app_name=app_name,
489
def get_login_success_callback(self):
490
"""Return the log in success cb."""
491
return self.controller.login_success_callback
493
def set_login_success_callback(self, cb):
494
"""Set log in success cb."""
495
self.controller.login_success_callback = cb
497
login_success_callback = property(get_login_success_callback,
498
set_login_success_callback)
500
def get_registration_success_callback(self):
501
"""Return the registration success cb."""
502
return self.controller.registration_success_callback
504
def set_registration_success_callback(self, cb):
505
"""Set registration success cb."""
506
self.controller.registration_success_callback = cb
508
registration_success_callback = property(get_registration_success_callback,
509
set_registration_success_callback)
511
def get_user_cancellation_callback(self):
512
"""Return the user cancellation callback."""
513
return self.controller.user_cancellation_callback
515
def set_user_cancellation_callback(self, cb):
516
"""Set the user cancellation callback."""
517
self.controller.user_cancellation_callback = cb
519
user_cancellation_callback = property(get_user_cancellation_callback,
520
set_user_cancellation_callback)