45
56
logger = setup_logging('ubuntu_sso.controllers')
59
class BackendController(object):
60
"""Represent a controller that talks with the sso backend."""
63
"""Create a new instance."""
68
"""Clean the resources."""
69
logger.info('Unregistering %s backends', len(self.backends))
70
for backend in self.backends:
71
backend.unregister_to_signals()
72
if self.root is not None:
73
logger.info('Disconnecting from root.')
74
self.root.disconnect()
77
def get_backend(self):
78
"""Return the backend used by the controller."""
79
# get the back end from the root
81
self.root = UbuntuSSOClient()
82
self.root = yield self.root.connect()
83
backend = self.root.sso_login
84
yield backend.register_to_signals()
85
self.backends.append(backend)
48
89
class ChooseSignInController(object):
49
90
"""Controlled to the ChooseSignIn view/widget."""
102
148
view.forgot_password_label.setText(FORGOTTEN_PASSWORD_BUTTON)
103
149
view.sign_in_button.setText(SIGN_IN_BUTTON)
151
def _connect_buttons(self, view, backend):
152
"""Connect the buttons to perform actions."""
153
view.sign_in_button.clicked.connect(lambda: self.login(view, backend))
154
# lets add call backs to be execute for the calls we are interested
155
backend.on_login_error_cb = lambda app, error:\
156
self.on_login_error(view, app, error)
157
backend.on_logged_in_cb = lambda app, result:\
158
self.on_logged_in(view, app, result)
160
def login(self, view, backend):
161
"""Perform the login using the backend."""
162
logger.debug('Trying to log in using %s', backend)
163
# grab the data from the view and call the backend
164
d = backend.login(self._app_name, view.email, view.password)
165
d.addErrback(lambda e: self.on_login_error(view, self._app_name, e))
167
def on_login_error(self, view, app_name, error):
168
"""There was an error when login in."""
170
logger.error('Got error when login %s, error: %s', app_name, error)
171
self.message_box.critical(view, app_name, error)
173
def on_logged_in(self, view, app_name, result):
174
"""We managed to log in."""
175
logger.info('Logged in for %s', app_name)
176
self.message_box.information(view, app_name, SUCCESS)
105
179
# use an ugly name just so have a simlar api as found in PyQt
106
180
#pylint: disable=C0103
107
182
def setupUi(self, view):
108
183
"""Setup the view."""
184
backend = yield self.get_backend()
109
185
view.setTitle(self._title)
110
186
self._set_translated_strings(view)
187
self._connect_buttons(view, backend)
111
188
#pylint: enable=C0103
114
class SetUpAccountController(object):
191
class SetUpAccountController(BackendController):
115
192
"""Conroller for the setup account view."""
117
194
def __init__(self, tos_id=0, validation_id=1, app_name='',
195
help_message='', message_box=None):
119
196
"""Create a new instance."""
197
super(SetUpAccountController, self).__init__()
198
if message_box is None:
199
message_box = QMessageBox
200
self.message_box = message_box
120
201
self._tos_id = tos_id
121
202
self._validation_id = validation_id
122
203
self._app_name = app_name
157
238
view.password_edit.textChanged.connect(
158
239
view.confirm_password_edit.textChanged.emit)
160
def _connect_ui_elements(self, view):
241
def _connect_ui_elements(self, view, backend):
161
242
"""Set the connection of signals."""
162
243
view.terms_and_conditions_button.clicked.connect(
163
244
lambda: self.set_next_tos(view))
164
245
view.set_up_button.clicked.connect(lambda: self.set_next_validation(
166
247
view.password_edit.textChanged.connect(
167
248
lambda x: self.update_password_strength(x, view))
168
249
view.terms_and_conditions_check_box.stateChanged.connect(
169
250
view.set_up_button.setEnabled)
251
view.captcha_refresh_label.linkActivated.connect(lambda url:\
252
self._refresh_captcha(view, backend))
253
# set the callbacks for the captcha generation
254
backend.on_captcha_generated_cb = lambda app, result:\
255
self.on_captcha_generated(view, app, result)
256
backend.on_captcha_generation_error_cb = lambda app, error:\
257
self.on_captcha_generation_error(view, app, error)
258
backend.on_user_registration_error_cb = lambda app, error:\
259
self.on_user_registration_error(view, app, error)
262
def _refresh_captcha(self, view, backend):
263
"""Refresh the captcha image shown in the ui."""
264
# lets clean behind us, do we have the old file arround?
266
if view.captcha_file and os.path.exists(view.captcha_file):
267
old_file = view.captcha_file
268
fd = tempfile.TemporaryFile(mode='r')
270
view.captcha_file = file_name
271
d = backend.generate_captcha(self._app_name, file_name)
273
d.addCallback(lambda x: os.unlink(old_file))
274
d.addErrback(lambda e: self.on_captcha_generation_error(view,
171
278
def _set_titles(self, view):
172
279
"""Set the diff titles of the view."""
173
280
view.setTitle(JOIN_HEADER_LABEL % {'app_name': self._app_name})
174
281
view.setSubTitle(self._help_message)
283
def on_captcha_generated(self, view, app_name, result):
284
"""A new image was generated."""
285
view.captcha_image = result
287
def on_captcha_generation_error(self, view, app_name, error):
288
"""An error ocurred."""
289
self.message_box.critical(view, app_name, str(error))
291
def on_user_registration_error(self, view, app_name, error):
292
"""Let the user know we could not register."""
293
# error are returned as a dict with the data we want to show.
294
self.message_box.critical(view, error['errtype'], error['email'])
176
296
def set_next_tos(self, view):
177
297
"""Set the tos page as the next one."""
178
298
view.next = self._tos_id
179
299
view.wizard().next()
181
def set_next_validation(self, view):
301
def validate_form(self, view):
302
"""Validate the info of the form and return an error."""
303
if not self.is_correct_email(view.email):
304
self.message_box.critical(view, self._app_name, EMAIL_INVALID)
305
if view.email_edit.text() != view.confirm_email_edit.text():
306
self.message_box.critical(view, self._app_name, EMAIL_MISMATCH)
308
if not is_min_required_password(str(view.password_edit.text())):
309
self.message_box.critical(view, self._app_name, PASSWORD_TOO_WEAK)
311
if view.password_edit.text() != view.confirm_password_edit.text():
312
self.message_box.critical(view, self._app_name, PASSWORD_MISMATCH)
316
def set_next_validation(self, view, backend):
182
317
"""Set the validation as the next page."""
183
view.next = self._validation_id
318
# validate the current info of the form, try to perform the action
319
# to register the user, and then move foward
320
if self.validate_form(view):
321
backend.register_user(self._app_name, view.email, view.password,
322
view.captcha_id, view.captcha_solution)
323
view.next = self._validation_id
186
326
def update_password_strength(self, password, view):
187
327
"""Callback used to update the password strenght UI code."""