Merge lp:~nataliabidart/ubuntu/precise/ubuntu-sso-client/ubuntu-sso-client-2.99.2 into lp:ubuntu/precise/ubuntu-sso-client
- Precise (12.04)
- ubuntu-sso-client-2.99.2
- Merge into precise
Proposed by
Natalia Bidart
Status: | Merged |
---|---|
Merged at revision: | 42 |
Proposed branch: | lp:~nataliabidart/ubuntu/precise/ubuntu-sso-client/ubuntu-sso-client-2.99.2 |
Merge into: | lp:ubuntu/precise/ubuntu-sso-client |
Diff against target: |
9351 lines (+4205/-3711) 42 files modified
PKG-INFO (+1/-1) debian/changelog (+7/-0) debian/watch (+1/-1) setup.py (+1/-1) ubuntu_sso/account.py (+11/-14) ubuntu_sso/credentials.py (+61/-25) ubuntu_sso/logger.py (+20/-0) ubuntu_sso/main/__init__.py (+257/-111) ubuntu_sso/main/linux.py (+181/-164) ubuntu_sso/main/tests/__init__.py (+37/-0) ubuntu_sso/main/tests/test_clients.py (+373/-0) ubuntu_sso/main/tests/test_common.py (+911/-155) ubuntu_sso/main/tests/test_linux.py (+0/-1275) ubuntu_sso/main/tests/test_windows.py (+18/-926) ubuntu_sso/main/windows.py (+185/-663) ubuntu_sso/networkstate/__init__.py (+8/-0) ubuntu_sso/networkstate/linux.py (+24/-0) ubuntu_sso/networkstate/tests/test_linux.py (+137/-2) ubuntu_sso/networkstate/tests/test_windows.py (+64/-0) ubuntu_sso/networkstate/windows.py (+13/-5) ubuntu_sso/qt/controllers.py (+12/-3) ubuntu_sso/qt/tests/__init__.py (+7/-4) ubuntu_sso/qt/tests/login_u_p.py (+37/-29) ubuntu_sso/qt/tests/show_gui.py (+36/-20) ubuntu_sso/qt/tests/test_controllers.py (+0/-2) ubuntu_sso/tests/__init__.py (+41/-0) ubuntu_sso/tests/test_account.py (+17/-3) ubuntu_sso/tests/test_credentials.py (+11/-14) ubuntu_sso/utils/ipc.py (+361/-0) ubuntu_sso/utils/tests/test_common.py (+264/-0) ubuntu_sso/utils/tests/test_ipc.py (+505/-0) ubuntu_sso/utils/tests/test_oauth_headers.py (+0/-260) ubuntu_sso/utils/tests/test_txsecrets.py (+22/-0) ubuntu_sso/utils/webclient/common.py (+16/-4) ubuntu_sso/utils/webclient/gsettings.py (+63/-0) ubuntu_sso/utils/webclient/libsoup.py (+32/-4) ubuntu_sso/utils/webclient/qtnetwork.py (+36/-6) ubuntu_sso/utils/webclient/restful.py (+52/-0) ubuntu_sso/utils/webclient/tests/test_gsettings.py (+131/-0) ubuntu_sso/utils/webclient/tests/test_restful.py (+148/-0) ubuntu_sso/utils/webclient/tests/test_webclient.py (+96/-16) ubuntu_sso/utils/webclient/txweb.py (+8/-3) |
To merge this branch: | bzr merge lp:~nataliabidart/ubuntu/precise/ubuntu-sso-client/ubuntu-sso-client-2.99.2 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Sebastien Bacher | Approve | ||
Review via email: mp+88926@code.launchpad.net |
Commit message
* New upstream release.
* debian/watch: updated to latest active milestone.
Description of the change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'PKG-INFO' | |||
2 | --- PKG-INFO 2012-01-03 19:02:32 +0000 | |||
3 | +++ PKG-INFO 2012-01-17 19:42:46 +0000 | |||
4 | @@ -1,6 +1,6 @@ | |||
5 | 1 | Metadata-Version: 1.1 | 1 | Metadata-Version: 1.1 |
6 | 2 | Name: ubuntu-sso-client | 2 | Name: ubuntu-sso-client |
8 | 3 | Version: 2.99.1 | 3 | Version: 2.99.2 |
9 | 4 | Summary: Ubuntu Single Sign-On client | 4 | Summary: Ubuntu Single Sign-On client |
10 | 5 | Home-page: https://launchpad.net/ubuntu-sso-client | 5 | Home-page: https://launchpad.net/ubuntu-sso-client |
11 | 6 | Author: Natalia Bidart | 6 | Author: Natalia Bidart |
12 | 7 | 7 | ||
13 | === modified file 'debian/changelog' | |||
14 | --- debian/changelog 2012-01-03 20:13:45 +0000 | |||
15 | +++ debian/changelog 2012-01-17 19:42:46 +0000 | |||
16 | @@ -1,3 +1,10 @@ | |||
17 | 1 | ubuntu-sso-client (2.99.2-0ubuntu1) UNRELEASED; urgency=low | ||
18 | 2 | |||
19 | 3 | * New upstream release. | ||
20 | 4 | * debian/watch: updated to latest active milestone. | ||
21 | 5 | |||
22 | 6 | -- Natalia Bidart (nessita) <nataliabidart@gmail.com> Tue, 17 Jan 2012 16:05:02 -0300 | ||
23 | 7 | |||
24 | 1 | ubuntu-sso-client (2.99.1-0ubuntu1) precise; urgency=low | 8 | ubuntu-sso-client (2.99.1-0ubuntu1) precise; urgency=low |
25 | 2 | 9 | ||
26 | 3 | * New upstream release: | 10 | * New upstream release: |
27 | 4 | 11 | ||
28 | === modified file 'debian/watch' | |||
29 | --- debian/watch 2012-01-03 19:01:50 +0000 | |||
30 | +++ debian/watch 2012-01-17 19:42:46 +0000 | |||
31 | @@ -1,2 +1,2 @@ | |||
32 | 1 | version=3 | 1 | version=3 |
34 | 2 | http://launchpad.net/ubuntu-sso-client/stable-3-0/2.99.1/ .*/ubuntu-sso-client-([0-9.]+)\.tar\.gz | 2 | http://launchpad.net/ubuntu-sso-client/stable-3-0/2.99.2/ .*/ubuntu-sso-client-([0-9.]+)\.tar\.gz |
35 | 3 | 3 | ||
36 | === modified file 'setup.py' | |||
37 | --- setup.py 2012-01-03 19:02:32 +0000 | |||
38 | +++ setup.py 2012-01-17 19:42:46 +0000 | |||
39 | @@ -364,7 +364,7 @@ | |||
40 | 364 | 364 | ||
41 | 365 | DistUtilsExtra.auto.setup( | 365 | DistUtilsExtra.auto.setup( |
42 | 366 | name='ubuntu-sso-client', | 366 | name='ubuntu-sso-client', |
44 | 367 | version='2.99.1', | 367 | version='2.99.2', |
45 | 368 | license='GPL v3', | 368 | license='GPL v3', |
46 | 369 | author='Natalia Bidart', | 369 | author='Natalia Bidart', |
47 | 370 | author_email='natalia.bidart@canonical.com', | 370 | author_email='natalia.bidart@canonical.com', |
48 | 371 | 371 | ||
49 | === modified file 'ubuntu_sso/account.py' | |||
50 | --- ubuntu_sso/account.py 2011-12-20 17:33:13 +0000 | |||
51 | +++ ubuntu_sso/account.py 2012-01-17 19:42:46 +0000 | |||
52 | @@ -96,15 +96,12 @@ | |||
53 | 96 | class Account(object): | 96 | class Account(object): |
54 | 97 | """Login and register users using the Ubuntu Single Sign On service.""" | 97 | """Login and register users using the Ubuntu Single Sign On service.""" |
55 | 98 | 98 | ||
57 | 99 | def __init__(self, sso_service_class=None): | 99 | def __init__(self, service_url=None): |
58 | 100 | """Create a new SSO Account manager.""" | 100 | """Create a new SSO Account manager.""" |
61 | 101 | if sso_service_class is None: | 101 | if service_url is not None: |
62 | 102 | self.sso_service_class = ServiceRoot | 102 | self.service_url = service_url |
63 | 103 | else: | 103 | else: |
68 | 104 | self.sso_service_class = sso_service_class | 104 | self.service_url = os.environ.get('USSOC_SERVICE_URL', SERVICE_URL) |
65 | 105 | |||
66 | 106 | self.service_url = os.environ.get('USSOC_SERVICE_URL', SERVICE_URL) | ||
67 | 107 | |||
69 | 108 | logger.info('Created a new SSO access layer for service url %r', | 105 | logger.info('Created a new SSO access layer for service url %r', |
70 | 109 | self.service_url) | 106 | self.service_url) |
71 | 110 | 107 | ||
72 | @@ -134,7 +131,7 @@ | |||
73 | 134 | """Generate a captcha using the SSO service.""" | 131 | """Generate a captcha using the SSO service.""" |
74 | 135 | logger.debug('generate_captcha: requesting captcha, filename: %r', | 132 | logger.debug('generate_captcha: requesting captcha, filename: %r', |
75 | 136 | filename) | 133 | filename) |
77 | 137 | sso_service = self.sso_service_class(None, self.service_url) | 134 | sso_service = ServiceRoot(None, self.service_url) |
78 | 138 | captcha = sso_service.captchas.new() | 135 | captcha = sso_service.captchas.new() |
79 | 139 | 136 | ||
80 | 140 | # download captcha and save to 'filename' | 137 | # download captcha and save to 'filename' |
81 | @@ -156,7 +153,7 @@ | |||
82 | 156 | logger.debug('register_user: email: %r password: <hidden>, ' | 153 | logger.debug('register_user: email: %r password: <hidden>, ' |
83 | 157 | 'displayname: %r, captcha_id: %r, captcha_solution: %r', | 154 | 'displayname: %r, captcha_id: %r, captcha_solution: %r', |
84 | 158 | email, displayname, captcha_id, captcha_solution) | 155 | email, displayname, captcha_id, captcha_solution) |
86 | 159 | sso_service = self.sso_service_class(None, self.service_url) | 156 | sso_service = ServiceRoot(None, self.service_url) |
87 | 160 | if not self._valid_email(email): | 157 | if not self._valid_email(email): |
88 | 161 | logger.error('register_user: InvalidEmailError for email: %r', | 158 | logger.error('register_user: InvalidEmailError for email: %r', |
89 | 162 | email) | 159 | email) |
90 | @@ -185,7 +182,7 @@ | |||
91 | 185 | logger.debug('login: email: %r password: <hidden>, token_name: %r', | 182 | logger.debug('login: email: %r password: <hidden>, token_name: %r', |
92 | 186 | email, token_name) | 183 | email, token_name) |
93 | 187 | basic = BasicHttpAuthorizer(email, password) | 184 | basic = BasicHttpAuthorizer(email, password) |
95 | 188 | sso_service = self.sso_service_class(basic, self.service_url) | 185 | sso_service = ServiceRoot(basic, self.service_url) |
96 | 189 | service = sso_service.authentications.authenticate | 186 | service = sso_service.authentications.authenticate |
97 | 190 | 187 | ||
98 | 191 | try: | 188 | try: |
99 | @@ -209,7 +206,7 @@ | |||
100 | 209 | token['consumer_key'], | 206 | token['consumer_key'], |
101 | 210 | token['consumer_secret'], | 207 | token['consumer_secret'], |
102 | 211 | oauth_token) | 208 | oauth_token) |
104 | 212 | sso_service = self.sso_service_class(authorizer, self.service_url) | 209 | sso_service = ServiceRoot(authorizer, self.service_url) |
105 | 213 | 210 | ||
106 | 214 | me_info = sso_service.accounts.me() | 211 | me_info = sso_service.accounts.me() |
107 | 215 | key = 'preferred_email' | 212 | key = 'preferred_email' |
108 | @@ -232,7 +229,7 @@ | |||
109 | 232 | token['consumer_key'], | 229 | token['consumer_key'], |
110 | 233 | token['consumer_secret'], | 230 | token['consumer_secret'], |
111 | 234 | oauth_token) | 231 | oauth_token) |
113 | 235 | sso_service = self.sso_service_class(authorizer, self.service_url) | 232 | sso_service = ServiceRoot(authorizer, self.service_url) |
114 | 236 | result = sso_service.accounts.validate_email(email_token=email_token) | 233 | result = sso_service.accounts.validate_email(email_token=email_token) |
115 | 237 | logger.info('validate_email: email: %r result: %r', email, result) | 234 | logger.info('validate_email: email: %r result: %r', email, result) |
116 | 238 | if 'errors' in result: | 235 | if 'errors' in result: |
117 | @@ -245,7 +242,7 @@ | |||
118 | 245 | 242 | ||
119 | 246 | def request_password_reset_token(self, email): | 243 | def request_password_reset_token(self, email): |
120 | 247 | """Request a token to reset the password for the account 'email'.""" | 244 | """Request a token to reset the password for the account 'email'.""" |
122 | 248 | sso_service = self.sso_service_class(None, self.service_url) | 245 | sso_service = ServiceRoot(None, self.service_url) |
123 | 249 | service = sso_service.registrations.request_password_reset_token | 246 | service = sso_service.registrations.request_password_reset_token |
124 | 250 | try: | 247 | try: |
125 | 251 | result = service(email=email) | 248 | result = service(email=email) |
126 | @@ -266,7 +263,7 @@ | |||
127 | 266 | 'request_password_reset_token'. | 263 | 'request_password_reset_token'. |
128 | 267 | 264 | ||
129 | 268 | """ | 265 | """ |
131 | 269 | sso_service = self.sso_service_class(None, self.service_url) | 266 | sso_service = ServiceRoot(None, self.service_url) |
132 | 270 | service = sso_service.registrations.set_new_password | 267 | service = sso_service.registrations.set_new_password |
133 | 271 | try: | 268 | try: |
134 | 272 | result = service(email=email, token=token, | 269 | result = service(email=email, token=token, |
135 | 273 | 270 | ||
136 | === modified file 'ubuntu_sso/credentials.py' | |||
137 | --- ubuntu_sso/credentials.py 2012-01-03 19:02:32 +0000 | |||
138 | +++ ubuntu_sso/credentials.py 2012-01-17 19:42:46 +0000 | |||
139 | @@ -44,7 +44,6 @@ | |||
140 | 44 | from functools import wraps | 44 | from functools import wraps |
141 | 45 | 45 | ||
142 | 46 | from twisted.internet import defer | 46 | from twisted.internet import defer |
143 | 47 | from twisted.internet.defer import inlineCallbacks, returnValue | ||
144 | 48 | 47 | ||
145 | 49 | from ubuntu_sso import NO_OP, utils | 48 | from ubuntu_sso import NO_OP, utils |
146 | 50 | from ubuntu_sso.keyring import Keyring | 49 | from ubuntu_sso.keyring import Keyring |
147 | @@ -105,7 +104,7 @@ | |||
148 | 105 | """Decorate 'f' to catch all errors.""" | 104 | """Decorate 'f' to catch all errors.""" |
149 | 106 | 105 | ||
150 | 107 | @wraps(f) | 106 | @wraps(f) |
152 | 108 | @inlineCallbacks | 107 | @defer.inlineCallbacks |
153 | 109 | def inner(self, *a, **kw): | 108 | def inner(self, *a, **kw): |
154 | 110 | """Call 'f' within a try-except block. | 109 | """Call 'f' within a try-except block. |
155 | 111 | 110 | ||
156 | @@ -123,7 +122,7 @@ | |||
157 | 123 | error_dict = {ERROR_KEY: msg, | 122 | error_dict = {ERROR_KEY: msg, |
158 | 124 | ERROR_DETAIL_KEY: traceback.format_exc()} | 123 | ERROR_DETAIL_KEY: traceback.format_exc()} |
159 | 125 | self.error_cb(error_dict) | 124 | self.error_cb(error_dict) |
161 | 126 | returnValue(result) | 125 | defer.returnValue(result) |
162 | 127 | 126 | ||
163 | 128 | return inner | 127 | return inner |
164 | 129 | 128 | ||
165 | @@ -185,7 +184,7 @@ | |||
166 | 185 | self.inner = None # will hold the GUI or SSOLoginRoot instance | 184 | self.inner = None # will hold the GUI or SSOLoginRoot instance |
167 | 186 | 185 | ||
168 | 187 | @handle_failures(msg='Problem while retrieving credentials') | 186 | @handle_failures(msg='Problem while retrieving credentials') |
170 | 188 | @inlineCallbacks | 187 | @defer.inlineCallbacks |
171 | 189 | def _login_success_cb(self, app_name, email): | 188 | def _login_success_cb(self, app_name, email): |
172 | 190 | """Store credentials when the login/registration succeeded. | 189 | """Store credentials when the login/registration succeeded. |
173 | 191 | 190 | ||
174 | @@ -209,7 +208,7 @@ | |||
175 | 209 | return | 208 | return |
176 | 210 | 209 | ||
177 | 211 | self.success_cb(creds) | 210 | self.success_cb(creds) |
179 | 212 | returnValue(0) | 211 | defer.returnValue(0) |
180 | 213 | 212 | ||
181 | 214 | def _auth_denial_cb(self, app_name): | 213 | def _auth_denial_cb(self, app_name): |
182 | 215 | """The user decided not to allow the registration or login.""" | 214 | """The user decided not to allow the registration or login.""" |
183 | @@ -265,16 +264,49 @@ | |||
184 | 265 | @handle_exceptions(msg='Problem logging with email and password.') | 264 | @handle_exceptions(msg='Problem logging with email and password.') |
185 | 266 | def _do_login(self, email, password): | 265 | def _do_login(self, email, password): |
186 | 267 | """Login using email/password, connect outcome signals.""" | 266 | """Login using email/password, connect outcome signals.""" |
194 | 268 | from ubuntu_sso.main import SSOLoginRoot | 267 | from ubuntu_sso.main import SSOLogin |
195 | 269 | self.inner = SSOLoginRoot() | 268 | |
196 | 270 | self.inner.login(app_name=self.app_name, email=email, | 269 | d = defer.Deferred() |
197 | 271 | password=password, | 270 | |
198 | 272 | result_cb=self._login_success_cb, | 271 | class DummyProxy(object): |
199 | 273 | error_cb=self._error_cb, | 272 | """A temporary proxy to handle non-traditional login.""" |
200 | 274 | not_validated_cb=self._error_cb) | 273 | |
201 | 274 | # pylint: disable=C0103 | ||
202 | 275 | |||
203 | 276 | def LoggedIn(self, app_name, result): | ||
204 | 277 | """User was logged in.""" | ||
205 | 278 | d.callback(result) | ||
206 | 279 | |||
207 | 280 | def LoginError(self, app_name, error): | ||
208 | 281 | """There was an error on login.""" | ||
209 | 282 | d.errback(error) | ||
210 | 283 | |||
211 | 284 | def UserNotValidated(self, app_name, email): | ||
212 | 285 | """User is not validated.""" | ||
213 | 286 | d.callback(None) | ||
214 | 287 | |||
215 | 288 | # pylint: enable=C0103 | ||
216 | 289 | |||
217 | 290 | self.inner = SSOLogin(proxy=DummyProxy()) | ||
218 | 291 | self.inner.login(app_name=self.app_name, | ||
219 | 292 | email=email, password=password) | ||
220 | 293 | |||
221 | 294 | def _success(result): | ||
222 | 295 | """Check if 'result' is a valid token, and callback properly.""" | ||
223 | 296 | if result is not None: | ||
224 | 297 | return self._login_success_cb(self.app_name, email) | ||
225 | 298 | else: | ||
226 | 299 | error_dict = { | ||
227 | 300 | 'errtype': 'UserNotValidated', | ||
228 | 301 | 'message': email, | ||
229 | 302 | } | ||
230 | 303 | self._error_cb(self.app_name, error_dict) | ||
231 | 304 | |||
232 | 305 | d.addCallback(_success) | ||
233 | 306 | d.addErrback(lambda f: self._error_cb(self.app_name, f.value)) | ||
234 | 275 | 307 | ||
235 | 276 | @handle_failures(msg='Problem while retrieving credentials') | 308 | @handle_failures(msg='Problem while retrieving credentials') |
237 | 277 | @inlineCallbacks | 309 | @defer.inlineCallbacks |
238 | 278 | def _login_or_register(self, login_only, email=None, password=None): | 310 | def _login_or_register(self, login_only, email=None, password=None): |
239 | 279 | """Get credentials if found else prompt the GUI.""" | 311 | """Get credentials if found else prompt the GUI.""" |
240 | 280 | logger.info("_login_or_register: login_only=%r email=%r.", | 312 | logger.info("_login_or_register: login_only=%r email=%r.", |
241 | @@ -303,20 +335,20 @@ | |||
242 | 303 | logger.debug('Calling success callback at %r.', self._success_cb) | 335 | logger.debug('Calling success callback at %r.', self._success_cb) |
243 | 304 | self._success_cb(self.app_name, creds) | 336 | self._success_cb(self.app_name, creds) |
244 | 305 | 337 | ||
246 | 306 | @inlineCallbacks | 338 | @defer.inlineCallbacks |
247 | 307 | def find_credentials(self): | 339 | def find_credentials(self): |
248 | 308 | """Get the credentials for 'self.app_name'. Return {} if not there.""" | 340 | """Get the credentials for 'self.app_name'. Return {} if not there.""" |
249 | 309 | creds = yield Keyring().get_credentials(self.app_name) | 341 | creds = yield Keyring().get_credentials(self.app_name) |
250 | 310 | logger.info('find_credentials: self.app_name %r, ' | 342 | logger.info('find_credentials: self.app_name %r, ' |
251 | 311 | 'result is {}? %s', self.app_name, creds is None) | 343 | 'result is {}? %s', self.app_name, creds is None) |
253 | 312 | returnValue(creds if creds is not None else {}) | 344 | defer.returnValue(creds if creds is not None else {}) |
254 | 313 | 345 | ||
256 | 314 | @inlineCallbacks | 346 | @defer.inlineCallbacks |
257 | 315 | def clear_credentials(self): | 347 | def clear_credentials(self): |
258 | 316 | """Clear the credentials for 'self.app_name'.""" | 348 | """Clear the credentials for 'self.app_name'.""" |
259 | 317 | yield Keyring().delete_credentials(self.app_name) | 349 | yield Keyring().delete_credentials(self.app_name) |
260 | 318 | 350 | ||
262 | 319 | @inlineCallbacks | 351 | @defer.inlineCallbacks |
263 | 320 | def store_credentials(self, token): | 352 | def store_credentials(self, token): |
264 | 321 | """Store the credentials for 'self.app_name'.""" | 353 | """Store the credentials for 'self.app_name'.""" |
265 | 322 | yield Keyring().set_credentials(self.app_name, token) | 354 | yield Keyring().set_credentials(self.app_name, token) |
266 | @@ -325,11 +357,15 @@ | |||
267 | 325 | """Get credentials if found else prompt the GUI to register.""" | 357 | """Get credentials if found else prompt the GUI to register.""" |
268 | 326 | return self._login_or_register(login_only=False) | 358 | return self._login_or_register(login_only=False) |
269 | 327 | 359 | ||
278 | 328 | def login(self): | 360 | def login(self, email=None, password=None): |
279 | 329 | """Get credentials if found else prompt the GUI to login.""" | 361 | """Get credentials if found else prompt the GUI to login. |
280 | 330 | return self._login_or_register(login_only=True) | 362 | |
281 | 331 | 363 | if 'email' and 'password' are given, do not prompt the user and use | |
282 | 332 | def login_email_password(self, email, password): | 364 | that to retrieve a token. |
283 | 333 | """Get credentials if found else login using email and password.""" | 365 | |
284 | 334 | return self._login_or_register(login_only=True, | 366 | """ |
285 | 335 | email=email, password=password) | 367 | if email is None or password is None: |
286 | 368 | return self._login_or_register(login_only=True) | ||
287 | 369 | else: | ||
288 | 370 | return self._login_or_register(login_only=True, | ||
289 | 371 | email=email, password=password) | ||
290 | 336 | 372 | ||
291 | === modified file 'ubuntu_sso/logger.py' | |||
292 | --- ubuntu_sso/logger.py 2011-12-20 17:33:13 +0000 | |||
293 | +++ ubuntu_sso/logger.py 2012-01-17 19:42:46 +0000 | |||
294 | @@ -25,6 +25,7 @@ | |||
295 | 25 | import os | 25 | import os |
296 | 26 | import sys | 26 | import sys |
297 | 27 | 27 | ||
298 | 28 | from functools import wraps | ||
299 | 28 | from logging.handlers import RotatingFileHandler | 29 | from logging.handlers import RotatingFileHandler |
300 | 29 | 30 | ||
301 | 30 | from ubuntu_sso.xdg_base_directory import unicode_path, xdg_cache_home | 31 | from ubuntu_sso.xdg_base_directory import unicode_path, xdg_cache_home |
302 | @@ -61,3 +62,22 @@ | |||
303 | 61 | logger.addHandler(debug_handler) | 62 | logger.addHandler(debug_handler) |
304 | 62 | 63 | ||
305 | 63 | return logger | 64 | return logger |
306 | 65 | |||
307 | 66 | |||
308 | 67 | def log_call(log_func): | ||
309 | 68 | """Decorator to log, using 'log_func', calls to functions.""" | ||
310 | 69 | |||
311 | 70 | def middle(f): | ||
312 | 71 | """Return a function that will act as 'f' but will log the call.""" | ||
313 | 72 | |||
314 | 73 | @wraps(f) | ||
315 | 74 | def inner(instance, *a, **kw): | ||
316 | 75 | """Call 'f(*a, **kw)' and return its result. Log that call.""" | ||
317 | 76 | log_func('%r: emitting %r with args %r and kwargs %r', | ||
318 | 77 | instance.__class__.__name__, f.__name__, a, kw) | ||
319 | 78 | result = f(instance, *a, **kw) | ||
320 | 79 | return result | ||
321 | 80 | |||
322 | 81 | return inner | ||
323 | 82 | |||
324 | 83 | return middle | ||
325 | 64 | 84 | ||
326 | === modified file 'ubuntu_sso/main/__init__.py' | |||
327 | --- ubuntu_sso/main/__init__.py 2011-12-20 17:33:13 +0000 | |||
328 | +++ ubuntu_sso/main/__init__.py 2012-01-17 19:42:46 +0000 | |||
329 | @@ -1,9 +1,5 @@ | |||
330 | 1 | # -*- coding: utf-8 -*- | 1 | # -*- coding: utf-8 -*- |
331 | 2 | # | 2 | # |
332 | 3 | # Author: Natalia Bidart <natalia.bidart@canonical.com> | ||
333 | 4 | # Author: Alejandro J. Cura <alecu@canonical.com> | ||
334 | 5 | # Author: Manuel de la Pena <manuel@canonical.com> | ||
335 | 6 | # | ||
336 | 7 | # Copyright 2011 Canonical Ltd. | 3 | # Copyright 2011 Canonical Ltd. |
337 | 8 | # | 4 | # |
338 | 9 | # This program is free software: you can redistribute it and/or modify it | 5 | # This program is free software: you can redistribute it and/or modify it |
339 | @@ -17,10 +13,18 @@ | |||
340 | 17 | # | 13 | # |
341 | 18 | # You should have received a copy of the GNU General Public License along | 14 | # You should have received a copy of the GNU General Public License along |
342 | 19 | # with this program. If not, see <http://www.gnu.org/licenses/>. | 15 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
344 | 20 | """Main object implementations.""" | 16 | """Single Sign On client main module. |
345 | 17 | |||
346 | 18 | Provides a utility which accepts requests to the Ubuntu Single Sign On | ||
347 | 19 | service. The OAuth process is handled, including adding the OAuth access token | ||
348 | 20 | to the local keyring. | ||
349 | 21 | |||
350 | 22 | """ | ||
351 | 21 | 23 | ||
352 | 22 | import sys | 24 | import sys |
353 | 23 | 25 | ||
354 | 26 | from twisted.internet import defer | ||
355 | 27 | |||
356 | 24 | from ubuntu_sso.account import Account | 28 | from ubuntu_sso.account import Account |
357 | 25 | from ubuntu_sso.credentials import ( | 29 | from ubuntu_sso.credentials import ( |
358 | 26 | Credentials, | 30 | Credentials, |
359 | @@ -35,16 +39,30 @@ | |||
360 | 35 | WINDOW_ID_KEY, | 39 | WINDOW_ID_KEY, |
361 | 36 | ) | 40 | ) |
362 | 37 | from ubuntu_sso.keyring import get_token_name, Keyring | 41 | from ubuntu_sso.keyring import get_token_name, Keyring |
364 | 38 | from ubuntu_sso.logger import setup_logging | 42 | from ubuntu_sso.logger import setup_logging, log_call |
365 | 39 | 43 | ||
366 | 40 | 44 | ||
367 | 41 | logger = setup_logging("ubuntu_sso.main") | 45 | logger = setup_logging("ubuntu_sso.main") |
368 | 42 | U1_PING_URL = "https://one.ubuntu.com/oauth/sso-finished-so-get-tokens/" | 46 | U1_PING_URL = "https://one.ubuntu.com/oauth/sso-finished-so-get-tokens/" |
369 | 43 | TIMEOUT_INTERVAL = 10000 # 10 seconds | 47 | TIMEOUT_INTERVAL = 10000 # 10 seconds |
370 | 44 | 48 | ||
371 | 49 | # pylint: disable=C0103 | ||
372 | 50 | |||
373 | 51 | if sys.platform == 'win32': | ||
374 | 52 | from ubuntu_sso.main import windows | ||
375 | 53 | source = windows | ||
376 | 54 | TIMEOUT_INTERVAL = 10000000000 # forever (hack) | ||
377 | 55 | else: | ||
378 | 56 | from ubuntu_sso.main import linux | ||
379 | 57 | source = linux | ||
380 | 58 | |||
381 | 59 | UbuntuSSOProxy = source.UbuntuSSOProxy | ||
382 | 60 | get_sso_client = source.get_sso_client | ||
383 | 61 | thread_execute = source.blocking | ||
384 | 62 | |||
385 | 45 | 63 | ||
386 | 46 | def except_to_errdict(e): | 64 | def except_to_errdict(e): |
388 | 47 | """Turn an exception into a dictionary to return thru DBus.""" | 65 | """Turn an exception into a dictionary to return thru IPC.""" |
389 | 48 | result = { | 66 | result = { |
390 | 49 | "errtype": e.__class__.__name__, | 67 | "errtype": e.__class__.__name__, |
391 | 50 | } | 68 | } |
392 | @@ -58,35 +76,71 @@ | |||
393 | 58 | return result | 76 | return result |
394 | 59 | 77 | ||
395 | 60 | 78 | ||
397 | 61 | class SSOLoginRoot(object): | 79 | class SSOLogin(object): |
398 | 62 | """Login thru the Single Sign On service.""" | 80 | """Login thru the Single Sign On service.""" |
399 | 63 | 81 | ||
402 | 64 | def __init__(self, sso_login_processor_class=Account, | 82 | def __init__(self, proxy): |
401 | 65 | sso_service_class=None): | ||
403 | 66 | """Initiate the Login object.""" | 83 | """Initiate the Login object.""" |
410 | 67 | self.sso_login_processor_class = sso_login_processor_class | 84 | self.processor = Account() |
411 | 68 | self.processor = self.sso_login_processor_class( | 85 | self.proxy = proxy |
412 | 69 | sso_service_class=sso_service_class) | 86 | |
413 | 70 | 87 | @log_call(logger.debug) | |
414 | 71 | def generate_captcha(self, app_name, filename, result_cb, | 88 | def CaptchaGenerated(self, app_name, result): |
415 | 72 | error_cb): | 89 | """Signal thrown after the captcha is generated.""" |
416 | 90 | self.proxy.CaptchaGenerated(app_name, result) | ||
417 | 91 | |||
418 | 92 | @log_call(logger.debug) | ||
419 | 93 | def CaptchaGenerationError(self, app_name, error): | ||
420 | 94 | """Signal thrown when there's a problem generating the captcha.""" | ||
421 | 95 | error_dict = except_to_errdict(error) | ||
422 | 96 | self.proxy.CaptchaGenerationError(app_name, error_dict) | ||
423 | 97 | |||
424 | 98 | def generate_captcha(self, app_name, filename): | ||
425 | 73 | """Call the matching method in the processor.""" | 99 | """Call the matching method in the processor.""" |
426 | 74 | def f(): | 100 | def f(): |
427 | 75 | """Inner function that will be run in a thread.""" | 101 | """Inner function that will be run in a thread.""" |
428 | 76 | return self.processor.generate_captcha(filename) | 102 | return self.processor.generate_captcha(filename) |
430 | 77 | thread_execute(f, app_name, result_cb, error_cb) | 103 | thread_execute(f, app_name, |
431 | 104 | self.CaptchaGenerated, self.CaptchaGenerationError) | ||
432 | 105 | |||
433 | 106 | @log_call(logger.debug) | ||
434 | 107 | def UserRegistered(self, app_name, result): | ||
435 | 108 | """Signal thrown when the user is registered.""" | ||
436 | 109 | self.proxy.UserRegistered(app_name, result) | ||
437 | 110 | |||
438 | 111 | @log_call(logger.debug) | ||
439 | 112 | def UserRegistrationError(self, app_name, error): | ||
440 | 113 | """Signal thrown when there's a problem registering the user.""" | ||
441 | 114 | error_dict = except_to_errdict(error) | ||
442 | 115 | self.proxy.UserRegistrationError(app_name, error_dict) | ||
443 | 78 | 116 | ||
444 | 79 | def register_user(self, app_name, email, password, name, captcha_id, | 117 | def register_user(self, app_name, email, password, name, captcha_id, |
446 | 80 | captcha_solution, result_cb, error_cb): | 118 | captcha_solution): |
447 | 81 | """Call the matching method in the processor.""" | 119 | """Call the matching method in the processor.""" |
448 | 82 | def f(): | 120 | def f(): |
449 | 83 | """Inner function that will be run in a thread.""" | 121 | """Inner function that will be run in a thread.""" |
450 | 84 | return self.processor.register_user(email, password, name, | 122 | return self.processor.register_user(email, password, name, |
451 | 85 | captcha_id, captcha_solution) | 123 | captcha_id, captcha_solution) |
456 | 86 | thread_execute(f, app_name, result_cb, error_cb) | 124 | thread_execute(f, app_name, |
457 | 87 | 125 | self.UserRegistered, self.UserRegistrationError) | |
458 | 88 | def login(self, app_name, email, password, result_cb, | 126 | |
459 | 89 | error_cb, not_validated_cb): | 127 | @log_call(logger.debug) |
460 | 128 | def LoggedIn(self, app_name, result): | ||
461 | 129 | """Signal thrown when the user is logged in.""" | ||
462 | 130 | self.proxy.LoggedIn(app_name, result) | ||
463 | 131 | |||
464 | 132 | @log_call(logger.debug) | ||
465 | 133 | def LoginError(self, app_name, error): | ||
466 | 134 | """Signal thrown when there is a problem in the login.""" | ||
467 | 135 | error_dict = except_to_errdict(error) | ||
468 | 136 | self.proxy.LoginError(app_name, error_dict) | ||
469 | 137 | |||
470 | 138 | @log_call(logger.debug) | ||
471 | 139 | def UserNotValidated(self, app_name, email): | ||
472 | 140 | """Signal thrown when the user is not validated.""" | ||
473 | 141 | self.proxy.UserNotValidated(app_name, email) | ||
474 | 142 | |||
475 | 143 | def login(self, app_name, email, password): | ||
476 | 90 | """Call the matching method in the processor.""" | 144 | """Call the matching method in the processor.""" |
477 | 91 | def f(): | 145 | def f(): |
478 | 92 | """Inner function that will be run in a thread.""" | 146 | """Inner function that will be run in a thread.""" |
479 | @@ -103,18 +157,25 @@ | |||
480 | 103 | is_validated = self.processor.is_validated(credentials) | 157 | is_validated = self.processor.is_validated(credentials) |
481 | 104 | logger.debug('user is validated? %r.', is_validated) | 158 | logger.debug('user is validated? %r.', is_validated) |
482 | 105 | if is_validated: | 159 | if is_validated: |
483 | 106 | # pylint: disable=E1101 | ||
484 | 107 | d = Keyring().set_credentials(app_name, credentials) | 160 | d = Keyring().set_credentials(app_name, credentials) |
489 | 108 | d.addCallback(lambda _: result_cb(app_name, email)) | 161 | d.addCallback(lambda _: self.LoggedIn(app_name, email)) |
490 | 109 | d.addErrback(lambda failure: \ | 162 | d.addErrback(lambda f: self.LoginError(app_name, f.value)) |
487 | 110 | error_cb(app_name, | ||
488 | 111 | except_to_errdict(failure.value))) | ||
491 | 112 | else: | 163 | else: |
497 | 113 | not_validated_cb(app_name, email) | 164 | self.UserNotValidated(app_name, email) |
498 | 114 | thread_execute(f, app_name, success_cb, error_cb) | 165 | thread_execute(f, app_name, success_cb, self.LoginError) |
499 | 115 | 166 | ||
500 | 116 | def validate_email(self, app_name, email, password, email_token, | 167 | @log_call(logger.debug) |
501 | 117 | result_cb, error_cb): | 168 | def EmailValidated(self, app_name, result): |
502 | 169 | """Signal thrown after the email is validated.""" | ||
503 | 170 | self.proxy.EmailValidated(app_name, result) | ||
504 | 171 | |||
505 | 172 | @log_call(logger.debug) | ||
506 | 173 | def EmailValidationError(self, app_name, error): | ||
507 | 174 | """Signal thrown when there's a problem validating the email.""" | ||
508 | 175 | error_dict = except_to_errdict(error) | ||
509 | 176 | self.proxy.EmailValidationError(app_name, error_dict) | ||
510 | 177 | |||
511 | 178 | def validate_email(self, app_name, email, password, email_token): | ||
512 | 118 | """Call the matching method in the processor.""" | 179 | """Call the matching method in the processor.""" |
513 | 119 | 180 | ||
514 | 120 | def f(): | 181 | def f(): |
515 | @@ -126,33 +187,54 @@ | |||
516 | 126 | 187 | ||
517 | 127 | def success_cb(app_name, credentials): | 188 | def success_cb(app_name, credentials): |
518 | 128 | """Validation finished successfully.""" | 189 | """Validation finished successfully.""" |
519 | 129 | # pylint: disable=E1101 | ||
520 | 130 | d = Keyring().set_credentials(app_name, credentials) | 190 | d = Keyring().set_credentials(app_name, credentials) |
523 | 131 | d.addCallback(lambda _: result_cb(app_name, email)) | 191 | d.addCallback(lambda _: self.EmailValidated(app_name, email)) |
524 | 132 | failure_cb = lambda f: error_cb(app_name, f.value) | 192 | failure_cb = lambda f: self.EmailValidationError(app_name, f.value) |
525 | 133 | d.addErrback(failure_cb) | 193 | d.addErrback(failure_cb) |
526 | 134 | 194 | ||
531 | 135 | thread_execute(f, app_name, success_cb, error_cb) | 195 | thread_execute(f, app_name, success_cb, self.EmailValidationError) |
532 | 136 | 196 | ||
533 | 137 | def request_password_reset_token(self, app_name, email, | 197 | @log_call(logger.debug) |
534 | 138 | result_cb, error_cb): | 198 | def PasswordResetTokenSent(self, app_name, result): |
535 | 199 | """Signal thrown when the token is succesfully sent.""" | ||
536 | 200 | self.proxy.PasswordResetTokenSent(app_name, result) | ||
537 | 201 | |||
538 | 202 | @log_call(logger.debug) | ||
539 | 203 | def PasswordResetError(self, app_name, error): | ||
540 | 204 | """Signal thrown when there's a problem sending the token.""" | ||
541 | 205 | error_dict = except_to_errdict(error) | ||
542 | 206 | self.proxy.PasswordResetError(app_name, error_dict) | ||
543 | 207 | |||
544 | 208 | def request_password_reset_token(self, app_name, email): | ||
545 | 139 | """Call the matching method in the processor.""" | 209 | """Call the matching method in the processor.""" |
546 | 140 | def f(): | 210 | def f(): |
547 | 141 | """Inner function that will be run in a thread.""" | 211 | """Inner function that will be run in a thread.""" |
548 | 142 | return self.processor.request_password_reset_token(email) | 212 | return self.processor.request_password_reset_token(email) |
553 | 143 | thread_execute(f, app_name, result_cb, error_cb) | 213 | thread_execute(f, app_name, |
554 | 144 | 214 | self.PasswordResetTokenSent, self.PasswordResetError) | |
555 | 145 | def set_new_password(self, app_name, email, token, new_password, | 215 | |
556 | 146 | result_cb, error_cb): | 216 | @log_call(logger.debug) |
557 | 217 | def PasswordChanged(self, app_name, result): | ||
558 | 218 | """Signal thrown when the token is succesfully sent.""" | ||
559 | 219 | self.proxy.PasswordChanged(app_name, result) | ||
560 | 220 | |||
561 | 221 | @log_call(logger.debug) | ||
562 | 222 | def PasswordChangeError(self, app_name, error): | ||
563 | 223 | """Signal thrown when there's a problem sending the token.""" | ||
564 | 224 | error_dict = except_to_errdict(error) | ||
565 | 225 | self.proxy.PasswordChangeError(app_name, error_dict) | ||
566 | 226 | |||
567 | 227 | def set_new_password(self, app_name, email, token, new_password): | ||
568 | 147 | """Call the matching method in the processor.""" | 228 | """Call the matching method in the processor.""" |
569 | 148 | def f(): | 229 | def f(): |
570 | 149 | """Inner function that will be run in a thread.""" | 230 | """Inner function that will be run in a thread.""" |
571 | 150 | return self.processor.set_new_password(email, token, | 231 | return self.processor.set_new_password(email, token, |
572 | 151 | new_password) | 232 | new_password) |
577 | 152 | thread_execute(f, app_name, result_cb, error_cb) | 233 | thread_execute(f, app_name, |
578 | 153 | 234 | self.PasswordChanged, self.PasswordChangeError) | |
579 | 154 | 235 | ||
580 | 155 | class CredentialsManagementRoot(object): | 236 | |
581 | 237 | class CredentialsManagement(object): | ||
582 | 156 | """Object that manages credentials. | 238 | """Object that manages credentials. |
583 | 157 | 239 | ||
584 | 158 | Every exposed method in this class requires one mandatory argument: | 240 | Every exposed method in this class requires one mandatory argument: |
585 | @@ -178,27 +260,12 @@ | |||
586 | 178 | 260 | ||
587 | 179 | """ | 261 | """ |
588 | 180 | 262 | ||
604 | 181 | def __init__(self, timeout_func, shutdown_func, found_cb, error_cb, | 263 | def __init__(self, timeout_func, shutdown_func, proxy): |
605 | 182 | denied_cb, *args, **kwargs): | 264 | super(CredentialsManagement, self).__init__() |
591 | 183 | """Create a new instance. | ||
592 | 184 | |||
593 | 185 | - 'found_cb' is a callback that will be executed when the credentials | ||
594 | 186 | were found. | ||
595 | 187 | |||
596 | 188 | - 'error_cb' is a callback that will be executed when there was an | ||
597 | 189 | error getting the credentials. | ||
598 | 190 | |||
599 | 191 | - 'denied_cb' is a callback that will be executed when the user denied | ||
600 | 192 | the use of the crendetials. | ||
601 | 193 | |||
602 | 194 | """ | ||
603 | 195 | super(CredentialsManagementRoot, self).__init__(*args, **kwargs) | ||
606 | 196 | self._ref_count = 0 | 265 | self._ref_count = 0 |
607 | 197 | self.timeout_func = timeout_func | 266 | self.timeout_func = timeout_func |
608 | 198 | self.shutdown_func = shutdown_func | 267 | self.shutdown_func = shutdown_func |
612 | 199 | self.found_cb = found_cb | 268 | self.proxy = proxy |
610 | 200 | self.error_cb = error_cb | ||
611 | 201 | self.denied_cb = denied_cb | ||
613 | 202 | 269 | ||
614 | 203 | def _get_ref_count(self): | 270 | def _get_ref_count(self): |
615 | 204 | """Get value of ref_count.""" | 271 | """Get value of ref_count.""" |
616 | @@ -236,12 +303,54 @@ | |||
617 | 236 | """Retrieve values from the generic param 'args'.""" | 303 | """Retrieve values from the generic param 'args'.""" |
618 | 237 | result = dict(i for i in args.iteritems() if i[0] in self.valid_keys) | 304 | result = dict(i for i in args.iteritems() if i[0] in self.valid_keys) |
619 | 238 | result[WINDOW_ID_KEY] = int(args.get(WINDOW_ID_KEY, 0)) | 305 | result[WINDOW_ID_KEY] = int(args.get(WINDOW_ID_KEY, 0)) |
623 | 239 | result[SUCCESS_CB_KEY] = self.found_cb | 306 | result[SUCCESS_CB_KEY] = self.CredentialsFound |
624 | 240 | result[ERROR_CB_KEY] = self.error_cb | 307 | result[ERROR_CB_KEY] = self.CredentialsError |
625 | 241 | result[DENIAL_CB_KEY] = self.denied_cb | 308 | result[DENIAL_CB_KEY] = self.AuthorizationDenied |
626 | 242 | return result | 309 | return result |
627 | 243 | 310 | ||
629 | 244 | def find_credentials(self, app_name, args, success_cb, error_cb): | 311 | @log_call(logger.info) |
630 | 312 | def AuthorizationDenied(self, app_name): | ||
631 | 313 | """Signal thrown when the user denies the authorization.""" | ||
632 | 314 | self.ref_count -= 1 | ||
633 | 315 | self.proxy.AuthorizationDenied(app_name) | ||
634 | 316 | |||
635 | 317 | # do not use log_call decorator since we should not log credentials | ||
636 | 318 | def CredentialsFound(self, app_name, credentials): | ||
637 | 319 | """Signal thrown when the credentials are found.""" | ||
638 | 320 | self.ref_count -= 1 | ||
639 | 321 | logger.info('%s: emitting CredentialsFound with app_name "%s".', | ||
640 | 322 | self.__class__.__name__, app_name) | ||
641 | 323 | self.proxy.CredentialsFound(app_name, credentials) | ||
642 | 324 | |||
643 | 325 | @log_call(logger.info) | ||
644 | 326 | def CredentialsNotFound(self, app_name): | ||
645 | 327 | """Signal thrown when the credentials are not found.""" | ||
646 | 328 | self.ref_count -= 1 | ||
647 | 329 | self.proxy.CredentialsNotFound(app_name) | ||
648 | 330 | |||
649 | 331 | @log_call(logger.info) | ||
650 | 332 | def CredentialsCleared(self, app_name): | ||
651 | 333 | """Signal thrown when the credentials were cleared.""" | ||
652 | 334 | self.ref_count -= 1 | ||
653 | 335 | self.proxy.CredentialsCleared(app_name) | ||
654 | 336 | |||
655 | 337 | @log_call(logger.info) | ||
656 | 338 | def CredentialsStored(self, app_name): | ||
657 | 339 | """Signal thrown when the credentials were cleared.""" | ||
658 | 340 | self.ref_count -= 1 | ||
659 | 341 | self.proxy.CredentialsStored(app_name) | ||
660 | 342 | |||
661 | 343 | @log_call(logger.error) | ||
662 | 344 | def CredentialsError(self, app_name, error): | ||
663 | 345 | """Signal thrown when there is a problem getting the credentials.""" | ||
664 | 346 | self.ref_count -= 1 | ||
665 | 347 | if isinstance(error, dict): | ||
666 | 348 | error_dict = error | ||
667 | 349 | else: | ||
668 | 350 | error_dict = except_to_errdict(error) | ||
669 | 351 | self.proxy.CredentialsError(app_name, error_dict) | ||
670 | 352 | |||
671 | 353 | def find_credentials(self, app_name, args, success_cb=None, error_cb=None): | ||
672 | 245 | """Look for the credentials for an application. | 354 | """Look for the credentials for an application. |
673 | 246 | 355 | ||
674 | 247 | - 'app_name': the name of the application which credentials are | 356 | - 'app_name': the name of the application which credentials are |
675 | @@ -249,21 +358,47 @@ | |||
676 | 249 | 358 | ||
677 | 250 | - 'args' is a dictionary, currently not used. | 359 | - 'args' is a dictionary, currently not used. |
678 | 251 | 360 | ||
680 | 252 | - 'success_cb' is a callback that will be execute if the operation was | 361 | - 'success_cb', if not None, will be executed if the operation was |
681 | 253 | a success. | 362 | a success. |
682 | 254 | 363 | ||
684 | 255 | - 'error_cb' is a callback that will be executed if the operation had | 364 | - 'error_cb', if not None, will be executed if the operation had |
685 | 256 | an error. | 365 | an error. |
686 | 257 | 366 | ||
687 | 258 | """ | 367 | """ |
688 | 368 | def _analize_creds(credentials): | ||
689 | 369 | """Find credentials and notify using signals.""" | ||
690 | 370 | if credentials is not None and len(credentials) > 0: | ||
691 | 371 | self.CredentialsFound(app_name, credentials) | ||
692 | 372 | else: | ||
693 | 373 | self.CredentialsNotFound(app_name) | ||
694 | 374 | |||
695 | 375 | def _tweaked_success_cb(creds): | ||
696 | 376 | """Decrease ref counter and call 'success_cb'.""" | ||
697 | 377 | self.ref_count -= 1 | ||
698 | 378 | success_cb(creds) | ||
699 | 379 | |||
700 | 380 | if success_cb is None: | ||
701 | 381 | _success_cb = _analize_creds | ||
702 | 382 | else: | ||
703 | 383 | _success_cb = _tweaked_success_cb | ||
704 | 384 | |||
705 | 385 | def _tweaked_error_cb(error, app): | ||
706 | 386 | """Decrease ref counter and call 'error_cb', modifying the dict.""" | ||
707 | 387 | self.ref_count -= 1 | ||
708 | 388 | error_cb(except_to_errdict(error.value)) | ||
709 | 389 | |||
710 | 390 | if error_cb is None: | ||
711 | 391 | _error_cb = lambda f, _: self.CredentialsError(app_name, f.value) | ||
712 | 392 | else: | ||
713 | 393 | _error_cb = _tweaked_error_cb | ||
714 | 394 | |||
715 | 259 | self.ref_count += 1 | 395 | self.ref_count += 1 |
716 | 260 | obj = Credentials(app_name) | 396 | obj = Credentials(app_name) |
717 | 261 | d = obj.find_credentials() | 397 | d = obj.find_credentials() |
721 | 262 | # pylint: disable=E1101 | 398 | d.addCallback(_success_cb) |
722 | 263 | d.addCallback(success_cb) | 399 | d.addErrback(_error_cb, app_name) |
720 | 264 | d.addErrback(error_cb, app_name) | ||
723 | 265 | 400 | ||
725 | 266 | def clear_credentials(self, app_name, args, success_cb, error_cb): | 401 | def clear_credentials(self, app_name, args): |
726 | 267 | """Clear the credentials for an application. | 402 | """Clear the credentials for an application. |
727 | 268 | 403 | ||
728 | 269 | - 'app_name': the name of the application which credentials are | 404 | - 'app_name': the name of the application which credentials are |
729 | @@ -271,21 +406,14 @@ | |||
730 | 271 | 406 | ||
731 | 272 | - 'args' is a dictionary, currently not used. | 407 | - 'args' is a dictionary, currently not used. |
732 | 273 | 408 | ||
733 | 274 | - 'success_cb' is a callback that will be execute if the operation was | ||
734 | 275 | a success. | ||
735 | 276 | |||
736 | 277 | - 'error_cb' is a callback that will be executed if the operation had | ||
737 | 278 | an error. | ||
738 | 279 | |||
739 | 280 | """ | 409 | """ |
740 | 281 | self.ref_count += 1 | 410 | self.ref_count += 1 |
741 | 282 | obj = Credentials(app_name) | 411 | obj = Credentials(app_name) |
742 | 283 | d = obj.clear_credentials() | 412 | d = obj.clear_credentials() |
746 | 284 | # pylint: disable=E1101 | 413 | d.addCallback(lambda _: self.CredentialsCleared(app_name)) |
747 | 285 | d.addCallback(success_cb) | 414 | d.addErrback(lambda f: self.CredentialsError(app_name, f.value)) |
745 | 286 | d.addErrback(error_cb, app_name) | ||
748 | 287 | 415 | ||
750 | 288 | def store_credentials(self, app_name, args, success_cb, error_cb): | 416 | def store_credentials(self, app_name, args): |
751 | 289 | """Store the token for an application. | 417 | """Store the token for an application. |
752 | 290 | 418 | ||
753 | 291 | - 'app_name': the name of the application which credentials are | 419 | - 'app_name': the name of the application which credentials are |
754 | @@ -295,18 +423,12 @@ | |||
755 | 295 | the following mandatory keys: 'token', 'token_key', 'consumer_key', | 423 | the following mandatory keys: 'token', 'token_key', 'consumer_key', |
756 | 296 | 'consumer_secret'. | 424 | 'consumer_secret'. |
757 | 297 | 425 | ||
758 | 298 | - 'success_cb' is a callback that will be execute if the operation was | ||
759 | 299 | a success. | ||
760 | 300 | |||
761 | 301 | - 'error_cb' is a callback that will be executed if the operation had | ||
762 | 302 | an error. | ||
763 | 303 | """ | 426 | """ |
764 | 304 | self.ref_count += 1 | 427 | self.ref_count += 1 |
765 | 305 | obj = Credentials(app_name) | 428 | obj = Credentials(app_name) |
766 | 306 | d = obj.store_credentials(args) | 429 | d = obj.store_credentials(args) |
770 | 307 | # pylint: disable=E1101 | 430 | d.addCallback(lambda _: self.CredentialsStored(app_name)) |
771 | 308 | d.addCallback(success_cb) | 431 | d.addErrback(lambda f: self.CredentialsError(app_name, f.value)) |
769 | 309 | d.addErrback(error_cb, app_name) | ||
772 | 310 | 432 | ||
773 | 311 | def register(self, app_name, args): | 433 | def register(self, app_name, args): |
774 | 312 | """Get credentials if found else prompt GUI to register.""" | 434 | """Get credentials if found else prompt GUI to register.""" |
775 | @@ -331,23 +453,47 @@ | |||
776 | 331 | email = args.pop('email') | 453 | email = args.pop('email') |
777 | 332 | password = args.pop('password') | 454 | password = args.pop('password') |
778 | 333 | obj = Credentials(app_name, **self._parse_args(args)) | 455 | obj = Credentials(app_name, **self._parse_args(args)) |
797 | 334 | obj.login_email_password(email=email, password=password) | 456 | obj.login(email=email, password=password) |
798 | 335 | 457 | ||
781 | 336 | |||
782 | 337 | # pylint: disable=C0103 | ||
783 | 338 | |||
784 | 339 | if sys.platform == 'win32': | ||
785 | 340 | from ubuntu_sso.main import windows | ||
786 | 341 | source = windows | ||
787 | 342 | TIMEOUT_INTERVAL = 10000000000 # forever | ||
788 | 343 | else: | ||
789 | 344 | from ubuntu_sso.main import linux | ||
790 | 345 | source = linux | ||
791 | 346 | |||
792 | 347 | CredentialsManagement = source.CredentialsManagement | ||
793 | 348 | get_sso_login_backend = source.get_sso_login_backend | ||
794 | 349 | main = source.main | ||
795 | 350 | SSOLogin = source.SSOLogin | ||
796 | 351 | thread_execute = source.blocking | ||
799 | 352 | 458 | ||
800 | 353 | # pylint: enable=C0103 | 459 | # pylint: enable=C0103 |
801 | 460 | |||
802 | 461 | class UbuntuSSOService(object): | ||
803 | 462 | """Manager that exposes the diff referenceable objects.""" | ||
804 | 463 | |||
805 | 464 | def __init__(self): | ||
806 | 465 | self.proxy = UbuntuSSOProxy(self) | ||
807 | 466 | self.sso_login = None | ||
808 | 467 | self.cred_manager = None | ||
809 | 468 | |||
810 | 469 | @defer.inlineCallbacks | ||
811 | 470 | def start(self): | ||
812 | 471 | """Start the service.""" | ||
813 | 472 | logger.debug('Starting up Ubuntu SSO service...') | ||
814 | 473 | try: | ||
815 | 474 | yield self.proxy.start() | ||
816 | 475 | except: | ||
817 | 476 | logger.exception('Can not start Ubuntu SSO service:') | ||
818 | 477 | raise | ||
819 | 478 | else: | ||
820 | 479 | logger.info('Ubuntu SSO service started.') | ||
821 | 480 | |||
822 | 481 | self.sso_login = SSOLogin(proxy=self.proxy.sso_login) | ||
823 | 482 | self.cred_manager = CredentialsManagement( | ||
824 | 483 | timeout_func=source.timeout_func, | ||
825 | 484 | shutdown_func=source.shutdown_func, | ||
826 | 485 | proxy=self.proxy.cred_manager) | ||
827 | 486 | |||
828 | 487 | def shutdown(self): | ||
829 | 488 | """Shutdown the service.""" | ||
830 | 489 | return self.proxy.shutdown() | ||
831 | 490 | |||
832 | 491 | |||
833 | 492 | def main(): | ||
834 | 493 | """Run the backend service.""" | ||
835 | 494 | logger.info('Setting up Ubuntu SSO service.') | ||
836 | 495 | source.start_setup() | ||
837 | 496 | service = UbuntuSSOService() | ||
838 | 497 | d = service.start() | ||
839 | 498 | d.addBoth(source.finish_setup) | ||
840 | 499 | source.main() | ||
841 | 354 | 500 | ||
842 | === modified file 'ubuntu_sso/main/linux.py' | |||
843 | --- ubuntu_sso/main/linux.py 2011-12-20 17:33:13 +0000 | |||
844 | +++ ubuntu_sso/main/linux.py 2012-01-17 19:42:46 +0000 | |||
845 | @@ -13,23 +13,26 @@ | |||
846 | 13 | # | 13 | # |
847 | 14 | # You should have received a copy of the GNU General Public License along | 14 | # You should have received a copy of the GNU General Public License along |
848 | 15 | # with this program. If not, see <http://www.gnu.org/licenses/>. | 15 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
855 | 16 | """Single Sign On login handler. | 16 | """Main module implementation specific for linux. |
856 | 17 | 17 | ||
857 | 18 | An utility which accepts requests for Ubuntu Single Sign On login over D-Bus. | 18 | This module should never import from the multiplatform one (main/__init__.py), |
858 | 19 | 19 | but the other way around. Likewise, this module should *not* have any logic | |
859 | 20 | The OAuth process is handled, including adding the OAuth access token to the | 20 | regarding error processing or decision making about when to send a given |
860 | 21 | local keyring. | 21 | signal. |
861 | 22 | |||
862 | 23 | Also, most of the logging is being made in the main module to avoid | ||
863 | 24 | duplication between the different platform implementations. | ||
864 | 22 | 25 | ||
865 | 23 | """ | 26 | """ |
866 | 24 | 27 | ||
867 | 25 | import threading | 28 | import threading |
868 | 26 | import signal | 29 | import signal |
869 | 27 | import sys | ||
870 | 28 | 30 | ||
871 | 29 | import dbus.mainloop.glib | 31 | import dbus.mainloop.glib |
872 | 30 | import dbus.service | 32 | import dbus.service |
873 | 31 | import gtk | 33 | import gtk |
874 | 32 | 34 | ||
875 | 35 | from twisted.internet import defer | ||
876 | 33 | 36 | ||
877 | 34 | from ubuntu_sso import ( | 37 | from ubuntu_sso import ( |
878 | 35 | DBUS_ACCOUNT_PATH, | 38 | DBUS_ACCOUNT_PATH, |
879 | @@ -39,13 +42,7 @@ | |||
880 | 39 | DBUS_IFACE_USER_NAME, | 42 | DBUS_IFACE_USER_NAME, |
881 | 40 | NO_OP, | 43 | NO_OP, |
882 | 41 | ) | 44 | ) |
883 | 42 | from ubuntu_sso.account import Account | ||
884 | 43 | from ubuntu_sso.logger import setup_logging | 45 | from ubuntu_sso.logger import setup_logging |
885 | 44 | from ubuntu_sso.main import ( | ||
886 | 45 | CredentialsManagementRoot, | ||
887 | 46 | SSOLoginRoot, | ||
888 | 47 | except_to_errdict, | ||
889 | 48 | ) | ||
890 | 49 | 46 | ||
891 | 50 | 47 | ||
892 | 51 | # Disable the invalid name warning, as we have a lot of DBus style names | 48 | # Disable the invalid name warning, as we have a lot of DBus style names |
893 | @@ -62,162 +59,124 @@ | |||
894 | 62 | try: | 59 | try: |
895 | 63 | result_cb(app_name, f()) | 60 | result_cb(app_name, f()) |
896 | 64 | except Exception, e: # pylint: disable=W0703 | 61 | except Exception, e: # pylint: disable=W0703 |
898 | 65 | msg = "Exception while running DBus blocking code in a thread:" | 62 | msg = "Exception while running blocking code in a thread:" |
899 | 66 | logger.exception(msg) | 63 | logger.exception(msg) |
901 | 67 | error_cb(app_name, except_to_errdict(e)) | 64 | error_cb(app_name, e) |
902 | 68 | threading.Thread(target=_in_thread).start() | 65 | threading.Thread(target=_in_thread).start() |
903 | 69 | 66 | ||
904 | 70 | 67 | ||
906 | 71 | class SSOLogin(dbus.service.Object): | 68 | class SSOLoginProxy(dbus.service.Object): |
907 | 72 | """Login thru the Single Sign On service.""" | 69 | """Login thru the Single Sign On service.""" |
908 | 73 | 70 | ||
909 | 74 | # Operator not preceded by a space (fails with dbus decorators) | 71 | # Operator not preceded by a space (fails with dbus decorators) |
910 | 75 | # pylint: disable=C0322 | 72 | # pylint: disable=C0322 |
911 | 76 | 73 | ||
915 | 77 | def __init__(self, bus_name, object_path=DBUS_ACCOUNT_PATH, | 74 | def __init__(self, root, *args, **kwargs): |
913 | 78 | sso_login_processor_class=Account, | ||
914 | 79 | sso_service_class=None): | ||
916 | 80 | """Initiate the Login object.""" | 75 | """Initiate the Login object.""" |
920 | 81 | dbus.service.Object.__init__(self, object_path=object_path, | 76 | super(SSOLoginProxy, self).__init__(*args, **kwargs) |
921 | 82 | bus_name=bus_name) | 77 | self.root = root |
919 | 83 | self.root = SSOLoginRoot(sso_login_processor_class, sso_service_class) | ||
922 | 84 | 78 | ||
923 | 85 | # generate_capcha signals | 79 | # generate_capcha signals |
924 | 86 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") | 80 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") |
925 | 87 | def CaptchaGenerated(self, app_name, result): | 81 | def CaptchaGenerated(self, app_name, result): |
926 | 88 | """Signal thrown after the captcha is generated.""" | 82 | """Signal thrown after the captcha is generated.""" |
927 | 89 | logger.debug('SSOLogin: emitting CaptchaGenerated with app_name "%s" ' | ||
928 | 90 | 'and result %r', app_name, result) | ||
929 | 91 | 83 | ||
930 | 92 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}") | 84 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}") |
931 | 93 | def CaptchaGenerationError(self, app_name, error): | 85 | def CaptchaGenerationError(self, app_name, error): |
932 | 94 | """Signal thrown when there's a problem generating the captcha.""" | 86 | """Signal thrown when there's a problem generating the captcha.""" |
933 | 95 | logger.debug('SSOLogin: emitting CaptchaGenerationError with ' | ||
934 | 96 | 'app_name "%s" and error %r', app_name, error) | ||
935 | 97 | 87 | ||
936 | 98 | @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME, | 88 | @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME, |
937 | 99 | in_signature='ss') | 89 | in_signature='ss') |
938 | 100 | def generate_captcha(self, app_name, filename): | 90 | def generate_captcha(self, app_name, filename): |
939 | 101 | """Call the matching method in the processor.""" | 91 | """Call the matching method in the processor.""" |
943 | 102 | self.root.generate_captcha(app_name, filename, | 92 | self.root.sso_login.generate_captcha(app_name, filename) |
941 | 103 | self.CaptchaGenerated, | ||
942 | 104 | self.CaptchaGenerationError) | ||
944 | 105 | 93 | ||
945 | 106 | # register_user signals | 94 | # register_user signals |
946 | 107 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") | 95 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") |
947 | 108 | def UserRegistered(self, app_name, result): | 96 | def UserRegistered(self, app_name, result): |
948 | 109 | """Signal thrown when the user is registered.""" | 97 | """Signal thrown when the user is registered.""" |
949 | 110 | logger.debug('SSOLogin: emitting UserRegistered with app_name "%s" ' | ||
950 | 111 | 'and result %r', app_name, result) | ||
951 | 112 | 98 | ||
952 | 113 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}") | 99 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}") |
953 | 114 | def UserRegistrationError(self, app_name, error): | 100 | def UserRegistrationError(self, app_name, error): |
954 | 115 | """Signal thrown when there's a problem registering the user.""" | 101 | """Signal thrown when there's a problem registering the user.""" |
955 | 116 | logger.debug('SSOLogin: emitting UserRegistrationError with ' | ||
956 | 117 | 'app_name "%s" and error %r', app_name, error) | ||
957 | 118 | 102 | ||
958 | 119 | @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME, | 103 | @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME, |
959 | 120 | in_signature='ssssss') | 104 | in_signature='ssssss') |
960 | 121 | def register_user(self, app_name, email, password, name, | 105 | def register_user(self, app_name, email, password, name, |
961 | 122 | captcha_id, captcha_solution): | 106 | captcha_id, captcha_solution): |
962 | 123 | """Call the matching method in the processor.""" | 107 | """Call the matching method in the processor.""" |
967 | 124 | self.root.register_user(app_name, email, password, name, captcha_id, | 108 | self.root.sso_login.register_user(app_name, email, password, name, |
968 | 125 | captcha_solution, | 109 | captcha_id, captcha_solution) |
965 | 126 | self.UserRegistered, | ||
966 | 127 | self.UserRegistrationError) | ||
969 | 128 | 110 | ||
970 | 129 | # login signals | 111 | # login signals |
971 | 130 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") | 112 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") |
972 | 131 | def LoggedIn(self, app_name, result): | 113 | def LoggedIn(self, app_name, result): |
973 | 132 | """Signal thrown when the user is logged in.""" | 114 | """Signal thrown when the user is logged in.""" |
974 | 133 | logger.debug('SSOLogin: emitting LoggedIn with app_name "%s" ' | ||
975 | 134 | 'and result %r', app_name, result) | ||
976 | 135 | 115 | ||
977 | 136 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}") | 116 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}") |
978 | 137 | def LoginError(self, app_name, error): | 117 | def LoginError(self, app_name, error): |
979 | 138 | """Signal thrown when there is a problem in the login.""" | 118 | """Signal thrown when there is a problem in the login.""" |
980 | 139 | logger.debug('SSOLogin: emitting LoginError with ' | ||
981 | 140 | 'app_name "%s" and error %r', app_name, error) | ||
982 | 141 | 119 | ||
983 | 142 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") | 120 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") |
984 | 143 | def UserNotValidated(self, app_name, result): | 121 | def UserNotValidated(self, app_name, result): |
985 | 144 | """Signal thrown when the user is not validated.""" | 122 | """Signal thrown when the user is not validated.""" |
986 | 145 | logger.debug('SSOLogin: emitting UserNotValidated with app_name "%s" ' | ||
987 | 146 | 'and result %r', app_name, result) | ||
988 | 147 | 123 | ||
989 | 148 | @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME, | 124 | @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME, |
990 | 149 | in_signature='sss') | 125 | in_signature='sss') |
991 | 150 | def login(self, app_name, email, password): | 126 | def login(self, app_name, email, password): |
992 | 151 | """Call the matching method in the processor.""" | 127 | """Call the matching method in the processor.""" |
995 | 152 | self.root.login(app_name, email, password, self.LoggedIn, | 128 | self.root.sso_login.login(app_name, email, password) |
994 | 153 | self.LoginError, self.UserNotValidated) | ||
996 | 154 | 129 | ||
997 | 155 | # validate_email signals | 130 | # validate_email signals |
998 | 156 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") | 131 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") |
999 | 157 | def EmailValidated(self, app_name, result): | 132 | def EmailValidated(self, app_name, result): |
1000 | 158 | """Signal thrown after the email is validated.""" | 133 | """Signal thrown after the email is validated.""" |
1001 | 159 | logger.debug('SSOLogin: emitting EmailValidated with app_name "%s" ' | ||
1002 | 160 | 'and result %r', app_name, result) | ||
1003 | 161 | 134 | ||
1004 | 162 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}") | 135 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}") |
1005 | 163 | def EmailValidationError(self, app_name, error): | 136 | def EmailValidationError(self, app_name, error): |
1006 | 164 | """Signal thrown when there's a problem validating the email.""" | 137 | """Signal thrown when there's a problem validating the email.""" |
1007 | 165 | logger.debug('SSOLogin: emitting EmailValidationError with ' | ||
1008 | 166 | 'app_name "%s" and error %r', app_name, error) | ||
1009 | 167 | 138 | ||
1010 | 168 | @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME, | 139 | @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME, |
1011 | 169 | in_signature='ssss') | 140 | in_signature='ssss') |
1012 | 170 | def validate_email(self, app_name, email, password, email_token): | 141 | def validate_email(self, app_name, email, password, email_token): |
1013 | 171 | """Call the matching method in the processor.""" | 142 | """Call the matching method in the processor.""" |
1017 | 172 | self.root.validate_email(app_name, email, password, email_token, | 143 | self.root.sso_login.validate_email(app_name, |
1018 | 173 | self.EmailValidated, | 144 | email, password, email_token) |
1016 | 174 | self.EmailValidationError) | ||
1019 | 175 | 145 | ||
1020 | 176 | # request_password_reset_token signals | 146 | # request_password_reset_token signals |
1021 | 177 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") | 147 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") |
1022 | 178 | def PasswordResetTokenSent(self, app_name, result): | 148 | def PasswordResetTokenSent(self, app_name, result): |
1023 | 179 | """Signal thrown when the token is succesfully sent.""" | 149 | """Signal thrown when the token is succesfully sent.""" |
1024 | 180 | logger.debug('SSOLogin: emitting PasswordResetTokenSent with app_name ' | ||
1025 | 181 | '"%s" and result %r', app_name, result) | ||
1026 | 182 | 150 | ||
1027 | 183 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}") | 151 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}") |
1028 | 184 | def PasswordResetError(self, app_name, error): | 152 | def PasswordResetError(self, app_name, error): |
1029 | 185 | """Signal thrown when there's a problem sending the token.""" | 153 | """Signal thrown when there's a problem sending the token.""" |
1030 | 186 | logger.debug('SSOLogin: emitting PasswordResetError with ' | ||
1031 | 187 | 'app_name "%s" and error %r', app_name, error) | ||
1032 | 188 | 154 | ||
1033 | 189 | @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME, | 155 | @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME, |
1034 | 190 | in_signature='ss') | 156 | in_signature='ss') |
1035 | 191 | def request_password_reset_token(self, app_name, email): | 157 | def request_password_reset_token(self, app_name, email): |
1036 | 192 | """Call the matching method in the processor.""" | 158 | """Call the matching method in the processor.""" |
1040 | 193 | self.root.request_password_reset_token(app_name, email, | 159 | self.root.sso_login.request_password_reset_token(app_name, email) |
1038 | 194 | self.PasswordResetTokenSent, | ||
1039 | 195 | self.PasswordResetError) | ||
1041 | 196 | 160 | ||
1042 | 197 | # set_new_password signals | 161 | # set_new_password signals |
1043 | 198 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") | 162 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") |
1044 | 199 | def PasswordChanged(self, app_name, result): | 163 | def PasswordChanged(self, app_name, result): |
1045 | 200 | """Signal thrown when the token is succesfully sent.""" | 164 | """Signal thrown when the token is succesfully sent.""" |
1046 | 201 | logger.debug('SSOLogin: emitting PasswordChanged with app_name "%s" ' | ||
1047 | 202 | 'and result %r', app_name, result) | ||
1048 | 203 | 165 | ||
1049 | 204 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}") | 166 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}") |
1050 | 205 | def PasswordChangeError(self, app_name, error): | 167 | def PasswordChangeError(self, app_name, error): |
1051 | 206 | """Signal thrown when there's a problem sending the token.""" | 168 | """Signal thrown when there's a problem sending the token.""" |
1052 | 207 | logger.debug('SSOLogin: emitting PasswordChangeError with ' | ||
1053 | 208 | 'app_name "%s" and error %r', app_name, error) | ||
1054 | 209 | 169 | ||
1055 | 210 | @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME, | 170 | @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME, |
1056 | 211 | in_signature='ssss') | 171 | in_signature='ssss') |
1057 | 212 | def set_new_password(self, app_name, email, token, new_password): | 172 | def set_new_password(self, app_name, email, token, new_password): |
1058 | 213 | """Call the matching method in the processor.""" | 173 | """Call the matching method in the processor.""" |
1066 | 214 | self.root.set_new_password(app_name, email, token, new_password, | 174 | self.root.sso_login.set_new_password(app_name, |
1067 | 215 | self.PasswordChanged, | 175 | email, token, new_password) |
1068 | 216 | self.PasswordChangeError) | 176 | |
1069 | 217 | 177 | ||
1070 | 218 | 178 | class CredentialsManagementProxy(dbus.service.Object): | |
1071 | 219 | class CredentialsManagement(dbus.service.Object): | 179 | """Object that manages credentials. |
1065 | 220 | """DBus object that manages credentials. | ||
1072 | 221 | 180 | ||
1073 | 222 | Every exposed method in this class requires one mandatory argument: | 181 | Every exposed method in this class requires one mandatory argument: |
1074 | 223 | 182 | ||
1075 | @@ -242,67 +201,36 @@ | |||
1076 | 242 | 201 | ||
1077 | 243 | """ | 202 | """ |
1078 | 244 | 203 | ||
1085 | 245 | def __init__(self, timeout_func, shutdown_func, *args, **kwargs): | 204 | def __init__(self, root, *args, **kwargs): |
1086 | 246 | super(CredentialsManagement, self).__init__(*args, **kwargs) | 205 | super(CredentialsManagementProxy, self).__init__(*args, **kwargs) |
1087 | 247 | self.root = CredentialsManagementRoot(timeout_func, shutdown_func, | 206 | self.root = root |
1082 | 248 | self.CredentialsFound, | ||
1083 | 249 | self.CredentialsError, | ||
1084 | 250 | self.AuthorizationDenied) | ||
1088 | 251 | 207 | ||
1089 | 252 | # Operator not preceded by a space (fails with dbus decorators) | 208 | # Operator not preceded by a space (fails with dbus decorators) |
1090 | 253 | # pylint: disable=C0322 | 209 | # pylint: disable=C0322 |
1091 | 254 | 210 | ||
1092 | 255 | def _process_failure(self, failure, app_name): | ||
1093 | 256 | """Process the 'failure' and emit CredentialsError.""" | ||
1094 | 257 | self.CredentialsError(app_name, except_to_errdict(failure.value)) | ||
1095 | 258 | |||
1096 | 259 | def shutdown(self): | ||
1097 | 260 | """If no ongoing requests, call self.shutdown_func.""" | ||
1098 | 261 | logger.debug('shutdown!, ref_count is %r.', self.root.ref_count) | ||
1099 | 262 | self.root.shutdown() | ||
1100 | 263 | |||
1101 | 264 | @dbus.service.signal(DBUS_CREDENTIALS_IFACE, signature='s') | 211 | @dbus.service.signal(DBUS_CREDENTIALS_IFACE, signature='s') |
1102 | 265 | def AuthorizationDenied(self, app_name): | 212 | def AuthorizationDenied(self, app_name): |
1103 | 266 | """Signal thrown when the user denies the authorization.""" | 213 | """Signal thrown when the user denies the authorization.""" |
1104 | 267 | self.root.ref_count -= 1 | ||
1105 | 268 | logger.info('%s: emitting AuthorizationDenied with app_name "%s".', | ||
1106 | 269 | self.__class__.__name__, app_name) | ||
1107 | 270 | 214 | ||
1108 | 271 | @dbus.service.signal(DBUS_CREDENTIALS_IFACE, signature='sa{ss}') | 215 | @dbus.service.signal(DBUS_CREDENTIALS_IFACE, signature='sa{ss}') |
1109 | 272 | def CredentialsFound(self, app_name, credentials): | 216 | def CredentialsFound(self, app_name, credentials): |
1110 | 273 | """Signal thrown when the credentials are found.""" | 217 | """Signal thrown when the credentials are found.""" |
1111 | 274 | self.root.ref_count -= 1 | ||
1112 | 275 | logger.info('%s: emitting CredentialsFound with app_name "%s".', | ||
1113 | 276 | self.__class__.__name__, app_name) | ||
1114 | 277 | 218 | ||
1115 | 278 | @dbus.service.signal(DBUS_CREDENTIALS_IFACE, signature='s') | 219 | @dbus.service.signal(DBUS_CREDENTIALS_IFACE, signature='s') |
1116 | 279 | def CredentialsNotFound(self, app_name): | 220 | def CredentialsNotFound(self, app_name): |
1117 | 280 | """Signal thrown when the credentials are not found.""" | 221 | """Signal thrown when the credentials are not found.""" |
1118 | 281 | self.root.ref_count -= 1 | ||
1119 | 282 | logger.info('%s: emitting CredentialsNotFound with app_name "%s".', | ||
1120 | 283 | self.__class__.__name__, app_name) | ||
1121 | 284 | 222 | ||
1122 | 285 | @dbus.service.signal(DBUS_CREDENTIALS_IFACE, signature='s') | 223 | @dbus.service.signal(DBUS_CREDENTIALS_IFACE, signature='s') |
1123 | 286 | def CredentialsCleared(self, app_name): | 224 | def CredentialsCleared(self, app_name): |
1124 | 287 | """Signal thrown when the credentials were cleared.""" | 225 | """Signal thrown when the credentials were cleared.""" |
1125 | 288 | self.root.ref_count -= 1 | ||
1126 | 289 | logger.info('%s: emitting CredentialsCleared with app_name "%s".', | ||
1127 | 290 | self.__class__.__name__, app_name) | ||
1128 | 291 | 226 | ||
1129 | 292 | @dbus.service.signal(DBUS_CREDENTIALS_IFACE, signature='s') | 227 | @dbus.service.signal(DBUS_CREDENTIALS_IFACE, signature='s') |
1130 | 293 | def CredentialsStored(self, app_name): | 228 | def CredentialsStored(self, app_name): |
1131 | 294 | """Signal thrown when the credentials were cleared.""" | 229 | """Signal thrown when the credentials were cleared.""" |
1132 | 295 | self.root.ref_count -= 1 | ||
1133 | 296 | logger.info('%s: emitting CredentialsStored with app_name "%s".', | ||
1134 | 297 | self.__class__.__name__, app_name) | ||
1135 | 298 | 230 | ||
1136 | 299 | @dbus.service.signal(DBUS_CREDENTIALS_IFACE, signature='sa{ss}') | 231 | @dbus.service.signal(DBUS_CREDENTIALS_IFACE, signature='sa{ss}') |
1137 | 300 | def CredentialsError(self, app_name, error_dict): | 232 | def CredentialsError(self, app_name, error_dict): |
1138 | 301 | """Signal thrown when there is a problem getting the credentials.""" | 233 | """Signal thrown when there is a problem getting the credentials.""" |
1139 | 302 | self.root.ref_count -= 1 | ||
1140 | 303 | logger.error('%s: emitting CredentialsError with app_name "%s" and ' | ||
1141 | 304 | 'error_dict %r.', self.__class__.__name__, app_name, | ||
1142 | 305 | error_dict) | ||
1143 | 306 | 234 | ||
1144 | 307 | @dbus.service.method(dbus_interface=DBUS_CREDENTIALS_IFACE, | 235 | @dbus.service.method(dbus_interface=DBUS_CREDENTIALS_IFACE, |
1145 | 308 | in_signature='sa{ss}', out_signature='') | 236 | in_signature='sa{ss}', out_signature='') |
1146 | @@ -315,16 +243,7 @@ | |||
1147 | 315 | - 'args' is a dictionary, currently not used. | 243 | - 'args' is a dictionary, currently not used. |
1148 | 316 | 244 | ||
1149 | 317 | """ | 245 | """ |
1160 | 318 | 246 | self.root.cred_manager.find_credentials(app_name, args) | |
1151 | 319 | def success_cb(credentials): | ||
1152 | 320 | """Find credentials and notify using signals.""" | ||
1153 | 321 | if credentials is not None and len(credentials) > 0: | ||
1154 | 322 | self.CredentialsFound(app_name, credentials) | ||
1155 | 323 | else: | ||
1156 | 324 | self.CredentialsNotFound(app_name) | ||
1157 | 325 | |||
1158 | 326 | self.root.find_credentials(app_name, args, success_cb, | ||
1159 | 327 | self._process_failure) | ||
1161 | 328 | 247 | ||
1162 | 329 | @dbus.service.method(dbus_interface=DBUS_CREDENTIALS_IFACE, | 248 | @dbus.service.method(dbus_interface=DBUS_CREDENTIALS_IFACE, |
1163 | 330 | in_signature="sa{ss}", out_signature="a{ss}", | 249 | in_signature="sa{ss}", out_signature="a{ss}", |
1164 | @@ -337,20 +256,13 @@ | |||
1165 | 337 | 256 | ||
1166 | 338 | """ | 257 | """ |
1167 | 339 | 258 | ||
1182 | 340 | def decrease_counter_success(credentials): | 259 | def _drop_dict(error_dict): |
1183 | 341 | """Call 'reply_handler' and decrease the root ref counter.""" | 260 | """Call 'error_handler' properly.""" |
1184 | 342 | reply_handler(credentials) | 261 | error_handler(dbus.service.DBusException(error_dict['errtype'])) |
1185 | 343 | self.root.ref_count -= 1 | 262 | |
1186 | 344 | 263 | self.root.cred_manager.find_credentials(app_name, args, | |
1187 | 345 | def decrease_counter_error(failure, app_name): | 264 | success_cb=reply_handler, |
1188 | 346 | """Call 'error_handler' and decrease the root ref counter.""" | 265 | error_cb=_drop_dict) |
1175 | 347 | error_dict = except_to_errdict(failure.value) | ||
1176 | 348 | error_handler(dbus.service.DBusException(error_dict)) | ||
1177 | 349 | self.root.ref_count -= 1 | ||
1178 | 350 | |||
1179 | 351 | self.root.find_credentials(app_name, args, | ||
1180 | 352 | success_cb=decrease_counter_success, | ||
1181 | 353 | error_cb=decrease_counter_error) | ||
1189 | 354 | 266 | ||
1190 | 355 | @dbus.service.method(dbus_interface=DBUS_CREDENTIALS_IFACE, | 267 | @dbus.service.method(dbus_interface=DBUS_CREDENTIALS_IFACE, |
1191 | 356 | in_signature='sa{ss}', out_signature='') | 268 | in_signature='sa{ss}', out_signature='') |
1192 | @@ -363,9 +275,7 @@ | |||
1193 | 363 | - 'args' is a dictionary, currently not used. | 275 | - 'args' is a dictionary, currently not used. |
1194 | 364 | 276 | ||
1195 | 365 | """ | 277 | """ |
1199 | 366 | self.root.clear_credentials(app_name, args, | 278 | self.root.cred_manager.clear_credentials(app_name, args) |
1197 | 367 | lambda _: self.CredentialsCleared(app_name), | ||
1198 | 368 | self._process_failure) | ||
1200 | 369 | 279 | ||
1201 | 370 | @dbus.service.method(dbus_interface=DBUS_CREDENTIALS_IFACE, | 280 | @dbus.service.method(dbus_interface=DBUS_CREDENTIALS_IFACE, |
1202 | 371 | in_signature='sa{ss}', out_signature='') | 281 | in_signature='sa{ss}', out_signature='') |
1203 | @@ -380,21 +290,19 @@ | |||
1204 | 380 | 'consumer_secret'. | 290 | 'consumer_secret'. |
1205 | 381 | 291 | ||
1206 | 382 | """ | 292 | """ |
1210 | 383 | self.root.store_credentials(app_name, args, | 293 | self.root.cred_manager.store_credentials(app_name, args) |
1208 | 384 | lambda _: self.CredentialsStored(app_name), | ||
1209 | 385 | self._process_failure) | ||
1211 | 386 | 294 | ||
1212 | 387 | @dbus.service.method(dbus_interface=DBUS_CREDENTIALS_IFACE, | 295 | @dbus.service.method(dbus_interface=DBUS_CREDENTIALS_IFACE, |
1213 | 388 | in_signature='sa{ss}', out_signature='') | 296 | in_signature='sa{ss}', out_signature='') |
1214 | 389 | def register(self, app_name, args): | 297 | def register(self, app_name, args): |
1215 | 390 | """Get credentials if found else prompt GUI to register.""" | 298 | """Get credentials if found else prompt GUI to register.""" |
1217 | 391 | self.root.register(app_name, args) | 299 | self.root.cred_manager.register(app_name, args) |
1218 | 392 | 300 | ||
1219 | 393 | @dbus.service.method(dbus_interface=DBUS_CREDENTIALS_IFACE, | 301 | @dbus.service.method(dbus_interface=DBUS_CREDENTIALS_IFACE, |
1220 | 394 | in_signature='sa{ss}', out_signature='') | 302 | in_signature='sa{ss}', out_signature='') |
1221 | 395 | def login(self, app_name, args): | 303 | def login(self, app_name, args): |
1222 | 396 | """Get credentials if found else prompt GUI to login.""" | 304 | """Get credentials if found else prompt GUI to login.""" |
1224 | 397 | self.root.login(app_name, args) | 305 | self.root.cred_manager.login(app_name, args) |
1225 | 398 | 306 | ||
1226 | 399 | @dbus.service.method(dbus_interface=DBUS_CREDENTIALS_IFACE, | 307 | @dbus.service.method(dbus_interface=DBUS_CREDENTIALS_IFACE, |
1227 | 400 | in_signature='sa{ss}', out_signature='') | 308 | in_signature='sa{ss}', out_signature='') |
1228 | @@ -406,12 +314,121 @@ | |||
1229 | 406 | returned trough the CredentialsFound signal. | 314 | returned trough the CredentialsFound signal. |
1230 | 407 | 315 | ||
1231 | 408 | """ | 316 | """ |
1238 | 409 | self.root.login_email_password(app_name, args) | 317 | self.root.cred_manager.login_email_password(app_name, args) |
1239 | 410 | 318 | ||
1240 | 411 | 319 | ||
1241 | 412 | def get_sso_login_backend(): | 320 | class UbuntuSSOProxy(object): |
1242 | 413 | """Get the backend for the Login service.""" | 321 | """Object that exposes the diff referenceable objects.""" |
1243 | 414 | raise NotImplementedError() | 322 | |
1244 | 323 | def __init__(self, root): | ||
1245 | 324 | self.root = root | ||
1246 | 325 | self.bus = dbus.SessionBus() | ||
1247 | 326 | self.sso_login = None | ||
1248 | 327 | self.cred_manager = None | ||
1249 | 328 | |||
1250 | 329 | def start(self): | ||
1251 | 330 | """Start listening, nothing async to be done in this platform.""" | ||
1252 | 331 | # Register DBus service for making sure we run only one instance | ||
1253 | 332 | name = self.bus.request_name(DBUS_BUS_NAME, | ||
1254 | 333 | dbus.bus.NAME_FLAG_DO_NOT_QUEUE) | ||
1255 | 334 | if name == dbus.bus.REQUEST_NAME_REPLY_EXISTS: | ||
1256 | 335 | raise AlreadyStartedError() | ||
1257 | 336 | |||
1258 | 337 | bus_name = dbus.service.BusName(DBUS_BUS_NAME, bus=self.bus) | ||
1259 | 338 | self.sso_login = SSOLoginProxy(self.root, | ||
1260 | 339 | bus_name=bus_name, | ||
1261 | 340 | object_path=DBUS_ACCOUNT_PATH) | ||
1262 | 341 | self.cred_manager = CredentialsManagementProxy(self.root, | ||
1263 | 342 | bus_name=bus_name, | ||
1264 | 343 | object_path=DBUS_CREDENTIALS_PATH) | ||
1265 | 344 | |||
1266 | 345 | return defer.succeed(None) | ||
1267 | 346 | |||
1268 | 347 | def shutdown(self): | ||
1269 | 348 | """Shutdown the service.""" | ||
1270 | 349 | self.sso_login.remove_from_connection() | ||
1271 | 350 | self.cred_manager.remove_from_connection() | ||
1272 | 351 | self.bus.release_name(DBUS_BUS_NAME) | ||
1273 | 352 | return defer.succeed(None) | ||
1274 | 353 | |||
1275 | 354 | |||
1276 | 355 | # ============================== client classes ============================== | ||
1277 | 356 | |||
1278 | 357 | |||
1279 | 358 | class RemoteClient(object): | ||
1280 | 359 | """Client that can perform calls to remote DBus object.""" | ||
1281 | 360 | |||
1282 | 361 | bus_name = None | ||
1283 | 362 | path = None | ||
1284 | 363 | interface = None | ||
1285 | 364 | |||
1286 | 365 | def __init__(self): | ||
1287 | 366 | self.bus = dbus.SessionBus() | ||
1288 | 367 | obj = self.bus.get_object(bus_name=self.bus_name, | ||
1289 | 368 | object_path=self.path, | ||
1290 | 369 | follow_name_owner_changes=True) | ||
1291 | 370 | self.dbus_iface = dbus.Interface(obj, dbus_interface=self.interface) | ||
1292 | 371 | self.dbus_iface.call_method = self.call_method | ||
1293 | 372 | self.dbus_iface.disconnect_from_signal = lambda _, sig: sig.remove() | ||
1294 | 373 | |||
1295 | 374 | def call_method(self, method_name, *args, **kwargs): | ||
1296 | 375 | """Call asynchronously 'method_name(*args)'. | ||
1297 | 376 | |||
1298 | 377 | Return a deferred that will be fired when the call finishes. | ||
1299 | 378 | |||
1300 | 379 | """ | ||
1301 | 380 | d = defer.Deferred() | ||
1302 | 381 | |||
1303 | 382 | reply_handler = kwargs.get('reply_handler', None) | ||
1304 | 383 | if reply_handler is not None: | ||
1305 | 384 | d.addCallback(lambda a: reply_handler(*a)) | ||
1306 | 385 | |||
1307 | 386 | error_handler = kwargs.get('error_handler', None) | ||
1308 | 387 | if error_handler is not None: | ||
1309 | 388 | d.addErrback(lambda f: error_handler(f.value)) | ||
1310 | 389 | |||
1311 | 390 | self.bus.call_async( | ||
1312 | 391 | bus_name=self.bus_name, object_path=self.path, | ||
1313 | 392 | dbus_interface=self.interface, method=method_name, | ||
1314 | 393 | signature=None, args=args, | ||
1315 | 394 | reply_handler=lambda *a: d.callback(a), | ||
1316 | 395 | error_handler=d.errback) | ||
1317 | 396 | |||
1318 | 397 | return d | ||
1319 | 398 | |||
1320 | 399 | |||
1321 | 400 | class SSOLoginClient(RemoteClient): | ||
1322 | 401 | """Access the UserManagement DBus interface.""" | ||
1323 | 402 | |||
1324 | 403 | bus_name = DBUS_BUS_NAME | ||
1325 | 404 | path = DBUS_ACCOUNT_PATH | ||
1326 | 405 | interface = DBUS_IFACE_USER_NAME | ||
1327 | 406 | |||
1328 | 407 | |||
1329 | 408 | class CredentialsManagementClient(RemoteClient): | ||
1330 | 409 | """Access the CredentialsManagement DBus interface.""" | ||
1331 | 410 | |||
1332 | 411 | bus_name = DBUS_BUS_NAME | ||
1333 | 412 | path = DBUS_CREDENTIALS_PATH | ||
1334 | 413 | interface = DBUS_CREDENTIALS_IFACE | ||
1335 | 414 | |||
1336 | 415 | |||
1337 | 416 | class UbuntuSSOClient(object): | ||
1338 | 417 | """Base client that provides remote access to the sso API.""" | ||
1339 | 418 | |||
1340 | 419 | def __init__(self): | ||
1341 | 420 | self.sso_login = SSOLoginClient().dbus_iface | ||
1342 | 421 | self.cred_manager = CredentialsManagementClient().dbus_iface | ||
1343 | 422 | |||
1344 | 423 | def disconnect(self): | ||
1345 | 424 | """No need to disconnect DBus proxy objects.""" | ||
1346 | 425 | return defer.succeed(None) | ||
1347 | 426 | |||
1348 | 427 | |||
1349 | 428 | def get_sso_client(): | ||
1350 | 429 | """Get a client to access the SSO service.""" | ||
1351 | 430 | result = UbuntuSSOClient() | ||
1352 | 431 | return defer.succeed(result) | ||
1353 | 415 | 432 | ||
1354 | 416 | 433 | ||
1355 | 417 | def sighup_handler(*a, **kw): | 434 | def sighup_handler(*a, **kw): |
1356 | @@ -422,32 +439,32 @@ | |||
1357 | 422 | # | 439 | # |
1358 | 423 | # gtk.main_quit and the logger methods are safe to be called from any | 440 | # gtk.main_quit and the logger methods are safe to be called from any |
1359 | 424 | # thread. Just don't call other random stuff here. | 441 | # thread. Just don't call other random stuff here. |
1361 | 425 | logger.info("Stoping Ubuntu SSO login manager since SIGHUP was received.") | 442 | logger.info("Stoping Ubuntu SSO service since SIGHUP was received.") |
1362 | 426 | gtk.main_quit() | 443 | gtk.main_quit() |
1363 | 427 | 444 | ||
1364 | 428 | 445 | ||
1367 | 429 | def main(): | 446 | class AlreadyStartedError(Exception): |
1368 | 430 | """Run the backend service.""" | 447 | """The backend service has already been started.""" |
1369 | 448 | |||
1370 | 449 | |||
1371 | 450 | timeout_func = gtk.timeout_add | ||
1372 | 451 | shutdown_func = gtk.main_quit | ||
1373 | 452 | |||
1374 | 453 | |||
1375 | 454 | def start_setup(): | ||
1376 | 455 | """Setup the env to run the service.""" | ||
1377 | 431 | dbus.mainloop.glib.threads_init() | 456 | dbus.mainloop.glib.threads_init() |
1378 | 432 | gtk.gdk.threads_init() | 457 | gtk.gdk.threads_init() |
1379 | 433 | dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) | 458 | dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) |
1380 | 434 | 459 | ||
1400 | 435 | bus = dbus.SessionBus() | 460 | |
1401 | 436 | # Register DBus service for making sure we run only one instance | 461 | def finish_setup(result): |
1402 | 437 | name = bus.request_name(DBUS_BUS_NAME, | 462 | """Run the specific mainloop only if no failure ocurred.""" |
1403 | 438 | dbus.bus.NAME_FLAG_DO_NOT_QUEUE) | 463 | if result is None: # no failure ocurred, start the service |
1404 | 439 | if name == dbus.bus.REQUEST_NAME_REPLY_EXISTS: | 464 | logger.debug("Hooking up SIGHUP with handler %r.", sighup_handler) |
1405 | 440 | logger.error("Ubuntu SSO login manager already running, quitting.") | 465 | signal.signal(signal.SIGHUP, sighup_handler) |
1406 | 441 | sys.exit(0) | 466 | gtk.main() |
1407 | 442 | 467 | ||
1408 | 443 | logger.debug("Hooking up SIGHUP with handler %r.", sighup_handler) | 468 | |
1409 | 444 | signal.signal(signal.SIGHUP, sighup_handler) | 469 | def main(): |
1410 | 445 | 470 | """Run the specific mainloop.""" | |
1392 | 446 | logger.info("Starting Ubuntu SSO login manager for bus %r.", DBUS_BUS_NAME) | ||
1393 | 447 | bus_name = dbus.service.BusName(DBUS_BUS_NAME, bus=dbus.SessionBus()) | ||
1394 | 448 | SSOLogin(bus_name, object_path=DBUS_ACCOUNT_PATH) | ||
1395 | 449 | CredentialsManagement(timeout_func=gtk.timeout_add, | ||
1396 | 450 | shutdown_func=gtk.main_quit, | ||
1397 | 451 | bus_name=bus_name, object_path=DBUS_CREDENTIALS_PATH) | ||
1398 | 452 | |||
1399 | 453 | gtk.main() | ||
1411 | 454 | 471 | ||
1412 | === modified file 'ubuntu_sso/main/tests/__init__.py' | |||
1413 | --- ubuntu_sso/main/tests/__init__.py 2011-03-22 23:29:20 +0000 | |||
1414 | +++ ubuntu_sso/main/tests/__init__.py 2012-01-17 19:42:46 +0000 | |||
1415 | @@ -15,3 +15,40 @@ | |||
1416 | 15 | # You should have received a copy of the GNU General Public License along | 15 | # You should have received a copy of the GNU General Public License along |
1417 | 16 | # with this program. If not, see <http://www.gnu.org/licenses/>. | 16 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
1418 | 17 | """Test the different main implementations.""" | 17 | """Test the different main implementations.""" |
1419 | 18 | |||
1420 | 19 | import sys | ||
1421 | 20 | |||
1422 | 21 | from twisted.internet import defer | ||
1423 | 22 | |||
1424 | 23 | from ubuntu_sso.tests import TOKEN | ||
1425 | 24 | |||
1426 | 25 | # Invalid name "BaseTestCase" | ||
1427 | 26 | # pylint: disable=C0103 | ||
1428 | 27 | |||
1429 | 28 | if sys.platform == 'win32': | ||
1430 | 29 | from ubuntu_sso.utils.tests.test_ipc import BaseIPCTestCase | ||
1431 | 30 | BaseTestCase = BaseIPCTestCase | ||
1432 | 31 | else: | ||
1433 | 32 | from ubuntuone.devtools.testcases.dbus import DBusTestCase | ||
1434 | 33 | BaseTestCase = DBusTestCase | ||
1435 | 34 | |||
1436 | 35 | # pylint: enable=C0103 | ||
1437 | 36 | |||
1438 | 37 | |||
1439 | 38 | class FakedCredentials(object): | ||
1440 | 39 | """A very dummy Credentials object.""" | ||
1441 | 40 | |||
1442 | 41 | def __init__(self, *a, **kw): | ||
1443 | 42 | self.login = self.register = lambda *a, **kw: None | ||
1444 | 43 | |||
1445 | 44 | def find_credentials(self, *a, **kw): | ||
1446 | 45 | """Retrieve credentials.""" | ||
1447 | 46 | return defer.succeed(TOKEN) | ||
1448 | 47 | |||
1449 | 48 | def clear_credentials(self, *a, **kw): | ||
1450 | 49 | """Clear credentials.""" | ||
1451 | 50 | return defer.succeed(None) | ||
1452 | 51 | |||
1453 | 52 | def store_credentials(self, *a, **kw): | ||
1454 | 53 | """Store credentials.""" | ||
1455 | 54 | return defer.succeed(None) | ||
1456 | 18 | 55 | ||
1457 | === added file 'ubuntu_sso/main/tests/test_clients.py' | |||
1458 | --- ubuntu_sso/main/tests/test_clients.py 1970-01-01 00:00:00 +0000 | |||
1459 | +++ ubuntu_sso/main/tests/test_clients.py 2012-01-17 19:42:46 +0000 | |||
1460 | @@ -0,0 +1,373 @@ | |||
1461 | 1 | # -*- coding: utf-8 -*- | ||
1462 | 2 | # | ||
1463 | 3 | # Copyright 2012 Canonical Ltd. | ||
1464 | 4 | # | ||
1465 | 5 | # This program is free software: you can redistribute it and/or modify it | ||
1466 | 6 | # under the terms of the GNU General Public License version 3, as published | ||
1467 | 7 | # by the Free Software Foundation. | ||
1468 | 8 | # | ||
1469 | 9 | # This program is distributed in the hope that it will be useful, but | ||
1470 | 10 | # WITHOUT ANY WARRANTY; without even the implied warranties of | ||
1471 | 11 | # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
1472 | 12 | # PURPOSE. See the GNU General Public License for more details. | ||
1473 | 13 | # | ||
1474 | 14 | # You should have received a copy of the GNU General Public License along | ||
1475 | 15 | # with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1476 | 16 | """Tests for the main SSO client code.""" | ||
1477 | 17 | |||
1478 | 18 | from twisted.internet import defer | ||
1479 | 19 | from ubuntuone.devtools.testcases import skipIfOS | ||
1480 | 20 | |||
1481 | 21 | from ubuntu_sso import main | ||
1482 | 22 | from ubuntu_sso.tests import ( | ||
1483 | 23 | APP_NAME, | ||
1484 | 24 | CAPTCHA_ID, | ||
1485 | 25 | CAPTCHA_SOLUTION, | ||
1486 | 26 | EMAIL, | ||
1487 | 27 | EMAIL_TOKEN, | ||
1488 | 28 | NAME, | ||
1489 | 29 | PASSWORD, | ||
1490 | 30 | TOKEN, | ||
1491 | 31 | ) | ||
1492 | 32 | from ubuntu_sso.main.tests import BaseTestCase, FakedCredentials | ||
1493 | 33 | |||
1494 | 34 | FILENAME = 'sample filename' | ||
1495 | 35 | |||
1496 | 36 | |||
1497 | 37 | class FakedKeyring(object): | ||
1498 | 38 | """A faked Keyring object.""" | ||
1499 | 39 | |||
1500 | 40 | _keys = {} | ||
1501 | 41 | |||
1502 | 42 | def get_credentials(self, app_name): | ||
1503 | 43 | """Return the credentials for app_name.""" | ||
1504 | 44 | return defer.succeed(self._keys.get(app_name, {})) | ||
1505 | 45 | |||
1506 | 46 | def delete_credentials(self, app_name): | ||
1507 | 47 | """Delete the credentials for app_name.""" | ||
1508 | 48 | self._keys.pop(app_name, None) | ||
1509 | 49 | return defer.succeed(None) | ||
1510 | 50 | |||
1511 | 51 | def set_credentials(self, app_name, token): | ||
1512 | 52 | """Store the credentials for app_name.""" | ||
1513 | 53 | self._keys[app_name] = token | ||
1514 | 54 | return defer.succeed(None) | ||
1515 | 55 | |||
1516 | 56 | |||
1517 | 57 | class AbstractTestCase(BaseTestCase): | ||
1518 | 58 | """The base test case with platform specific support.""" | ||
1519 | 59 | |||
1520 | 60 | timeout = 2 | ||
1521 | 61 | method = None | ||
1522 | 62 | backend_method = method | ||
1523 | 63 | params = () | ||
1524 | 64 | success_signal = None | ||
1525 | 65 | backend_result = None | ||
1526 | 66 | success_result = None | ||
1527 | 67 | error_signal = None | ||
1528 | 68 | |||
1529 | 69 | @defer.inlineCallbacks | ||
1530 | 70 | def setUp(self): | ||
1531 | 71 | yield super(AbstractTestCase, self).setUp() | ||
1532 | 72 | self.keyring = FakedKeyring() | ||
1533 | 73 | self.patch(main, 'Keyring', lambda: self.keyring) | ||
1534 | 74 | |||
1535 | 75 | # avoid putting stuff in the mainloops | ||
1536 | 76 | self.patch(main.source, 'timeout_func', lambda *a: None) | ||
1537 | 77 | self.patch(main.source, 'shutdown_func', lambda *a: None) | ||
1538 | 78 | |||
1539 | 79 | self.sso_service = main.UbuntuSSOService() | ||
1540 | 80 | yield self.sso_service.start() | ||
1541 | 81 | self.addCleanup(self.sso_service.shutdown) | ||
1542 | 82 | |||
1543 | 83 | self.sso_client = yield main.get_sso_client() | ||
1544 | 84 | self.addCleanup(self.sso_client.disconnect) | ||
1545 | 85 | |||
1546 | 86 | self.client = self.patchable_backend = None | ||
1547 | 87 | |||
1548 | 88 | if self.backend_method is None: | ||
1549 | 89 | self.backend_method = self.method | ||
1550 | 90 | |||
1551 | 91 | def _backend_succeed(self, *args, **kwargs): | ||
1552 | 92 | """Make self.patchable_backend return self.backend_result.""" | ||
1553 | 93 | return self.backend_result | ||
1554 | 94 | |||
1555 | 95 | def _backend_fail(self, *args, **kwargs): | ||
1556 | 96 | """Make self.patchable_backend raise an exception.""" | ||
1557 | 97 | raise ValueError((args, kwargs)) | ||
1558 | 98 | |||
1559 | 99 | @defer.inlineCallbacks | ||
1560 | 100 | def assert_method_correct(self, success_signal, error_signal, | ||
1561 | 101 | patched_backend_method, expected_result=None): | ||
1562 | 102 | """Calling 'self.method' works ok. | ||
1563 | 103 | |||
1564 | 104 | Check that 'success_signal' is emitted, and make the test fail if | ||
1565 | 105 | error_signal is received. | ||
1566 | 106 | |||
1567 | 107 | The self.patchable_backend will be patched with | ||
1568 | 108 | 'patched_backend_method'. | ||
1569 | 109 | |||
1570 | 110 | """ | ||
1571 | 111 | if self.method is None: | ||
1572 | 112 | defer.returnValue(None) | ||
1573 | 113 | |||
1574 | 114 | d = defer.Deferred() | ||
1575 | 115 | |||
1576 | 116 | cb = lambda *a: d.callback(a) | ||
1577 | 117 | match = self.client.connect_to_signal(success_signal, cb) | ||
1578 | 118 | self.addCleanup(self.client.disconnect_from_signal, | ||
1579 | 119 | success_signal, match) | ||
1580 | 120 | |||
1581 | 121 | eb = lambda *a: d.errback(AssertionError(a)) | ||
1582 | 122 | match = self.client.connect_to_signal(error_signal, eb) | ||
1583 | 123 | self.addCleanup(self.client.disconnect_from_signal, | ||
1584 | 124 | error_signal, match) | ||
1585 | 125 | |||
1586 | 126 | self.patch(self.patchable_backend, self.backend_method, | ||
1587 | 127 | patched_backend_method) | ||
1588 | 128 | |||
1589 | 129 | yield self.client.call_method(self.method, *self.params) | ||
1590 | 130 | |||
1591 | 131 | result = yield d | ||
1592 | 132 | self.assertEqual(expected_result, result) | ||
1593 | 133 | |||
1594 | 134 | def test_success(self): | ||
1595 | 135 | """Test that the 'method' works ok.""" | ||
1596 | 136 | success_signal = self.success_signal | ||
1597 | 137 | error_signal = self.error_signal | ||
1598 | 138 | patched_backend_method = self._backend_succeed | ||
1599 | 139 | expected_result = self.success_result | ||
1600 | 140 | |||
1601 | 141 | return self.assert_method_correct(success_signal, error_signal, | ||
1602 | 142 | patched_backend_method, expected_result) | ||
1603 | 143 | |||
1604 | 144 | def test_error(self): | ||
1605 | 145 | """Test that the 'method' fails as expected.""" | ||
1606 | 146 | success_signal = self.error_signal | ||
1607 | 147 | error_signal = self.success_signal | ||
1608 | 148 | patched_backend_method = self._backend_fail | ||
1609 | 149 | expected_result = (APP_NAME, dict(errtype='ValueError')) | ||
1610 | 150 | |||
1611 | 151 | return self.assert_method_correct(success_signal, error_signal, | ||
1612 | 152 | patched_backend_method, expected_result) | ||
1613 | 153 | |||
1614 | 154 | |||
1615 | 155 | class SSOLoginProxyTestCase(AbstractTestCase): | ||
1616 | 156 | """Test the SSOLoginProxy interface.""" | ||
1617 | 157 | |||
1618 | 158 | @defer.inlineCallbacks | ||
1619 | 159 | def setUp(self): | ||
1620 | 160 | yield super(SSOLoginProxyTestCase, self).setUp() | ||
1621 | 161 | self.client = self.sso_client.sso_login | ||
1622 | 162 | self.patchable_backend = self.sso_service.sso_login.processor | ||
1623 | 163 | |||
1624 | 164 | |||
1625 | 165 | class GenerateCaptchaTestCase(SSOLoginProxyTestCase): | ||
1626 | 166 | """Test the generate_captcha method.""" | ||
1627 | 167 | |||
1628 | 168 | method = 'generate_captcha' | ||
1629 | 169 | params = (APP_NAME, FILENAME) | ||
1630 | 170 | success_signal = 'CaptchaGenerated' | ||
1631 | 171 | error_signal = 'CaptchaGenerationError' | ||
1632 | 172 | backend_result = 'a captcha id' | ||
1633 | 173 | success_result = (APP_NAME, backend_result) | ||
1634 | 174 | |||
1635 | 175 | |||
1636 | 176 | class RegisterUserTestCase(SSOLoginProxyTestCase): | ||
1637 | 177 | """Test the register_user method.""" | ||
1638 | 178 | |||
1639 | 179 | method = 'register_user' | ||
1640 | 180 | params = (APP_NAME, EMAIL, PASSWORD, NAME, CAPTCHA_ID, CAPTCHA_SOLUTION) | ||
1641 | 181 | success_signal = 'UserRegistered' | ||
1642 | 182 | error_signal = 'UserRegistrationError' | ||
1643 | 183 | backend_result = EMAIL | ||
1644 | 184 | success_result = (APP_NAME, backend_result) | ||
1645 | 185 | |||
1646 | 186 | |||
1647 | 187 | class LoginTestCase(SSOLoginProxyTestCase): | ||
1648 | 188 | """Test the login method.""" | ||
1649 | 189 | |||
1650 | 190 | method = 'login' | ||
1651 | 191 | params = (APP_NAME, EMAIL, PASSWORD) | ||
1652 | 192 | success_signal = 'LoggedIn' | ||
1653 | 193 | error_signal = 'LoginError' | ||
1654 | 194 | backend_result = TOKEN | ||
1655 | 195 | success_result = (APP_NAME, EMAIL) | ||
1656 | 196 | |||
1657 | 197 | @defer.inlineCallbacks | ||
1658 | 198 | def test_success(self): | ||
1659 | 199 | """Test that the 'method' works ok when the user is validated.""" | ||
1660 | 200 | self.patch(self.patchable_backend, 'is_validated', lambda _: True) | ||
1661 | 201 | |||
1662 | 202 | yield super(LoginTestCase, self).test_success() | ||
1663 | 203 | |||
1664 | 204 | expected_credentials = yield self.keyring.get_credentials(APP_NAME) | ||
1665 | 205 | self.assertEqual(expected_credentials, TOKEN) | ||
1666 | 206 | |||
1667 | 207 | def test_not_validated(self): | ||
1668 | 208 | """Test that the 'method' works ok when the user is not validated.""" | ||
1669 | 209 | self.patch(self.patchable_backend, 'is_validated', lambda _: False) | ||
1670 | 210 | self.patch(self, 'success_signal', 'UserNotValidated') | ||
1671 | 211 | |||
1672 | 212 | return super(LoginTestCase, self).test_success() | ||
1673 | 213 | |||
1674 | 214 | def test_error_when_setting_credentials(self): | ||
1675 | 215 | """The 'error_signal' is emitted when credentials can't be set.""" | ||
1676 | 216 | self.patch(self.patchable_backend, 'is_validated', lambda _: True) | ||
1677 | 217 | exc = TypeError('foo') | ||
1678 | 218 | self.patch(self.keyring, 'set_credentials', lambda *a: defer.fail(exc)) | ||
1679 | 219 | patched_backend_method = lambda *a, **kw: self.backend_result | ||
1680 | 220 | expected_result = (APP_NAME, dict(errtype='TypeError', message='foo')) | ||
1681 | 221 | |||
1682 | 222 | return self.assert_method_correct('LoginError', 'LoggedIn', | ||
1683 | 223 | patched_backend_method, expected_result) | ||
1684 | 224 | |||
1685 | 225 | |||
1686 | 226 | class ValidateEmailTestCase(SSOLoginProxyTestCase): | ||
1687 | 227 | """Test the validate_email method.""" | ||
1688 | 228 | |||
1689 | 229 | method = 'validate_email' | ||
1690 | 230 | params = (APP_NAME, EMAIL, PASSWORD, EMAIL_TOKEN) | ||
1691 | 231 | success_signal = 'EmailValidated' | ||
1692 | 232 | error_signal = 'EmailValidationError' | ||
1693 | 233 | backend_result = TOKEN | ||
1694 | 234 | success_result = (APP_NAME, EMAIL) | ||
1695 | 235 | |||
1696 | 236 | |||
1697 | 237 | class RequestPasswordResetTokenTestCase(SSOLoginProxyTestCase): | ||
1698 | 238 | """Test the request_password_reset_token method.""" | ||
1699 | 239 | |||
1700 | 240 | method = 'request_password_reset_token' | ||
1701 | 241 | params = (APP_NAME, EMAIL) | ||
1702 | 242 | success_signal = 'PasswordResetTokenSent' | ||
1703 | 243 | error_signal = 'PasswordResetError' | ||
1704 | 244 | backend_result = EMAIL | ||
1705 | 245 | success_result = (APP_NAME, backend_result) | ||
1706 | 246 | |||
1707 | 247 | |||
1708 | 248 | class SetNewPasswordTestCase(SSOLoginProxyTestCase): | ||
1709 | 249 | """Test the set_new_password method.""" | ||
1710 | 250 | |||
1711 | 251 | method = 'set_new_password' | ||
1712 | 252 | params = (APP_NAME, EMAIL, EMAIL_TOKEN, PASSWORD) | ||
1713 | 253 | success_signal = 'PasswordChanged' | ||
1714 | 254 | error_signal = 'PasswordChangeError' | ||
1715 | 255 | backend_result = EMAIL | ||
1716 | 256 | success_result = (APP_NAME, backend_result) | ||
1717 | 257 | |||
1718 | 258 | |||
1719 | 259 | class CredentialsManagementProxyTestCase(AbstractTestCase): | ||
1720 | 260 | """Tests for the CredentialsManagementProxy DBus interface.""" | ||
1721 | 261 | |||
1722 | 262 | error_signal = 'CredentialsError' | ||
1723 | 263 | args = dict(foo='bar', fuh='baz') | ||
1724 | 264 | params = (APP_NAME, args) | ||
1725 | 265 | |||
1726 | 266 | @defer.inlineCallbacks | ||
1727 | 267 | def setUp(self): | ||
1728 | 268 | yield super(CredentialsManagementProxyTestCase, self).setUp() | ||
1729 | 269 | self.credentials = FakedCredentials() | ||
1730 | 270 | self.patch(main, 'Credentials', lambda *a, **kw: self.credentials) | ||
1731 | 271 | |||
1732 | 272 | self.client = self.sso_client.cred_manager | ||
1733 | 273 | self.patchable_backend = self.credentials | ||
1734 | 274 | |||
1735 | 275 | def _backend_succeed(self, *args, **kwargs): | ||
1736 | 276 | """Make self.patchable_backend return self.backend_result.""" | ||
1737 | 277 | return defer.succeed(self.backend_result) | ||
1738 | 278 | |||
1739 | 279 | def _backend_fail(self, *args, **kwargs): | ||
1740 | 280 | """Make self.patchable_backend return a failed deferred.""" | ||
1741 | 281 | return defer.fail(ValueError((args, kwargs))) | ||
1742 | 282 | |||
1743 | 283 | |||
1744 | 284 | class FindCredentialsTestCase(CredentialsManagementProxyTestCase): | ||
1745 | 285 | """Test the find_credentials method.""" | ||
1746 | 286 | |||
1747 | 287 | method = 'find_credentials' | ||
1748 | 288 | success_signal = 'CredentialsFound' | ||
1749 | 289 | backend_result = TOKEN | ||
1750 | 290 | success_result = (APP_NAME, backend_result) | ||
1751 | 291 | |||
1752 | 292 | @skipIfOS('win32', 'find_credentials_sync is only provided in Linux ' | ||
1753 | 293 | 'due to compatibility issues with old clients.') | ||
1754 | 294 | @defer.inlineCallbacks | ||
1755 | 295 | def test_find_credentials_sync(self): | ||
1756 | 296 | """The credentials are asked and returned in a sync call.""" | ||
1757 | 297 | d = defer.Deferred() | ||
1758 | 298 | |||
1759 | 299 | self.client.call_method('find_credentials_sync', | ||
1760 | 300 | APP_NAME, self.args, | ||
1761 | 301 | reply_handler=d.callback, | ||
1762 | 302 | error_handler=d.errback) | ||
1763 | 303 | creds = yield d | ||
1764 | 304 | self.assertEqual(creds, TOKEN) | ||
1765 | 305 | |||
1766 | 306 | @skipIfOS('win32', 'find_credentials_sync is only provided in Linux ' | ||
1767 | 307 | 'due to compatibility issues with old clients.') | ||
1768 | 308 | @defer.inlineCallbacks | ||
1769 | 309 | def test_find_credentials_sync_error(self): | ||
1770 | 310 | """If find_credentials_sync fails, error_handler is called.""" | ||
1771 | 311 | self.patch(self.credentials, 'find_credentials', self._backend_fail) | ||
1772 | 312 | d = defer.Deferred() | ||
1773 | 313 | |||
1774 | 314 | self.client.call_method('find_credentials_sync', | ||
1775 | 315 | APP_NAME, self.args, | ||
1776 | 316 | reply_handler=d.errback, | ||
1777 | 317 | error_handler=d.callback) | ||
1778 | 318 | error = yield d | ||
1779 | 319 | error = error.args[0] | ||
1780 | 320 | self.assertEqual(error, 'ValueError') | ||
1781 | 321 | |||
1782 | 322 | |||
1783 | 323 | class ClearCredentialsTestCase(CredentialsManagementProxyTestCase): | ||
1784 | 324 | """Test the clear_credentials method.""" | ||
1785 | 325 | |||
1786 | 326 | method = 'clear_credentials' | ||
1787 | 327 | success_signal = 'CredentialsCleared' | ||
1788 | 328 | success_result = (APP_NAME,) | ||
1789 | 329 | |||
1790 | 330 | |||
1791 | 331 | class StoreCredentialsTestCase(CredentialsManagementProxyTestCase): | ||
1792 | 332 | """Test the store_credentials method.""" | ||
1793 | 333 | |||
1794 | 334 | method = 'store_credentials' | ||
1795 | 335 | success_signal = 'CredentialsStored' | ||
1796 | 336 | success_result = (APP_NAME,) | ||
1797 | 337 | |||
1798 | 338 | |||
1799 | 339 | class CredentialsManagementOpsTestCase(CredentialsManagementProxyTestCase): | ||
1800 | 340 | """Tests for the CredentialsManagementProxy login/register methods.""" | ||
1801 | 341 | |||
1802 | 342 | success_signal = 'CredentialsFound' | ||
1803 | 343 | success_result = (APP_NAME, TOKEN) | ||
1804 | 344 | |||
1805 | 345 | def _backend_succeed(self, *args, **kwargs): | ||
1806 | 346 | """Make self.patchable_backend return self.backend_result.""" | ||
1807 | 347 | self.sso_service.cred_manager.CredentialsFound(APP_NAME, TOKEN) | ||
1808 | 348 | |||
1809 | 349 | def _backend_fail(self, *args, **kwargs): | ||
1810 | 350 | """Make self.patchable_backend fail.""" | ||
1811 | 351 | self.sso_service.cred_manager.CredentialsError(APP_NAME, | ||
1812 | 352 | dict(errtype='ValueError')) | ||
1813 | 353 | |||
1814 | 354 | |||
1815 | 355 | class RegisterTestCase(CredentialsManagementOpsTestCase): | ||
1816 | 356 | """Test the register method.""" | ||
1817 | 357 | |||
1818 | 358 | method = 'register' | ||
1819 | 359 | |||
1820 | 360 | |||
1821 | 361 | class LoginOnlyTestCase(CredentialsManagementOpsTestCase): | ||
1822 | 362 | """Test the login method.""" | ||
1823 | 363 | |||
1824 | 364 | method = 'login' | ||
1825 | 365 | |||
1826 | 366 | |||
1827 | 367 | class LoginEmailPasswordTestCase(CredentialsManagementOpsTestCase): | ||
1828 | 368 | """Test the login method.""" | ||
1829 | 369 | |||
1830 | 370 | method = 'login_email_password' | ||
1831 | 371 | backend_method = 'login' | ||
1832 | 372 | args = dict(email=EMAIL, password=PASSWORD) | ||
1833 | 373 | params = (APP_NAME, args) | ||
1834 | 0 | 374 | ||
1835 | === modified file 'ubuntu_sso/main/tests/test_common.py' | |||
1836 | --- ubuntu_sso/main/tests/test_common.py 2011-12-20 17:33:13 +0000 | |||
1837 | +++ ubuntu_sso/main/tests/test_common.py 2012-01-17 19:42:46 +0000 | |||
1838 | @@ -1,12 +1,6 @@ | |||
1839 | 1 | # -*- coding: utf-8 -*- | 1 | # -*- coding: utf-8 -*- |
1840 | 2 | # | 2 | # |
1848 | 3 | # test_main - tests for ubuntu_sso.main | 3 | # Copyright 2009-2011 Canonical Ltd. |
1842 | 4 | # | ||
1843 | 5 | # Author: Natalia Bidart <natalia.bidart@canonical.com> | ||
1844 | 6 | # Author: Alejandro J. Cura <alecu@canonical.com> | ||
1845 | 7 | # Author: Manuel de la Pena <manuel@canonical.com> | ||
1846 | 8 | # | ||
1847 | 9 | # Copyright 2009-2010 Canonical Ltd. | ||
1849 | 10 | # | 4 | # |
1850 | 11 | # This program is free software: you can redistribute it and/or modify it | 5 | # This program is free software: you can redistribute it and/or modify it |
1851 | 12 | # under the terms of the GNU General Public License version 3, as published | 6 | # under the terms of the GNU General Public License version 3, as published |
1852 | @@ -19,161 +13,923 @@ | |||
1853 | 19 | # | 13 | # |
1854 | 20 | # You should have received a copy of the GNU General Public License along | 14 | # You should have received a copy of the GNU General Public License along |
1855 | 21 | # with this program. If not, see <http://www.gnu.org/licenses/>. | 15 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
1862 | 22 | """Tests share by diff platforms.""" | 16 | """Tests for the main SSO client code.""" |
1863 | 23 | 17 | ||
1864 | 24 | from unittest import TestCase | 18 | import logging |
1865 | 25 | 19 | ||
1866 | 26 | from mocker import MockerTestCase, MATCH | 20 | from twisted.internet import defer |
1867 | 27 | 21 | from ubuntuone.devtools.handlers import MementoHandler | |
1868 | 22 | |||
1869 | 23 | from ubuntu_sso import main | ||
1870 | 28 | from ubuntu_sso.main import ( | 24 | from ubuntu_sso.main import ( |
1871 | 29 | CredentialsManagement, | 25 | CredentialsManagement, |
1872 | 26 | except_to_errdict, | ||
1873 | 30 | SSOLogin, | 27 | SSOLogin, |
1874 | 28 | thread_execute, | ||
1875 | 29 | TIMEOUT_INTERVAL, | ||
1876 | 30 | UbuntuSSOService, | ||
1877 | 31 | ) | 31 | ) |
1976 | 32 | 32 | from ubuntu_sso.main import (HELP_TEXT_KEY, PING_URL_KEY, | |
1977 | 33 | 33 | TC_URL_KEY, UI_CLASS_KEY, UI_MODULE_KEY, WINDOW_ID_KEY, | |
1978 | 34 | class SSOLoginMockedTestCase(MockerTestCase): | 34 | SUCCESS_CB_KEY, ERROR_CB_KEY, DENIAL_CB_KEY) |
1979 | 35 | """Test that the call are relied correctly.""" | 35 | from ubuntu_sso.main.tests import FakedCredentials |
1980 | 36 | 36 | from ubuntu_sso.tests import (APP_NAME, TC_URL, HELP_TEXT, CAPTCHA_ID, | |
1981 | 37 | def setUp(self): | 37 | CAPTCHA_SOLUTION, EMAIL, EMAIL_TOKEN, NAME, PASSWORD, PING_URL, TOKEN, |
1982 | 38 | """Setup tests.""" | 38 | WINDOW_ID, Recorder, TestCase) |
1983 | 39 | super(SSOLoginMockedTestCase, self).setUp() | 39 | |
1984 | 40 | self.root = self.mocker.mock() | 40 | |
1985 | 41 | mockbusname = self.mocker.mock() | 41 | # Access to a protected member 'yyy' of a client class |
1986 | 42 | mockbus = self.mocker.mock() | 42 | # pylint: disable=W0212 |
1987 | 43 | mockbusname.get_bus() | 43 | |
1988 | 44 | self.mocker.result(mockbus) | 44 | |
1989 | 45 | self.login = SSOLogin(mockbus) | 45 | class SampleException(Exception): |
1990 | 46 | self.login.root = self.root | 46 | """The exception that will be thrown by the fake thread_execute.""" |
1991 | 47 | self.mocker.reset() | 47 | |
1992 | 48 | 48 | ||
1993 | 49 | def test_generate_captcha(self): | 49 | def fake_ok_thread_execute(f, app, cb, eb): |
1994 | 50 | """Test that the call is relayed.""" | 50 | """A fake thread_execute function that succeeds.""" |
1995 | 51 | app_name = 'app' | 51 | cb(app, f()) |
1996 | 52 | filename = 'file' | 52 | |
1997 | 53 | self.root.generate_captcha(app_name, filename, | 53 | |
1998 | 54 | MATCH(callable), MATCH(callable)) | 54 | def fake_err_thread_execute(f, app, cb, eb): |
1999 | 55 | self.mocker.replay() | 55 | """A fake thread_execute function that fails.""" |
2000 | 56 | self.login.generate_captcha(app_name, filename) | 56 | try: |
2001 | 57 | 57 | f() | |
2002 | 58 | def test_register_user(self): | 58 | except Exception, e: # pylint: disable=W0703 |
2003 | 59 | """Test that the call is relayed.""" | 59 | eb(app, except_to_errdict(e)) |
2004 | 60 | app_name = 'app' | 60 | else: |
2005 | 61 | email = 'email' | 61 | eb(app, except_to_errdict(SampleException())) |
2006 | 62 | password = 'pwd' | 62 | |
2007 | 63 | name = 'display name' | 63 | |
2008 | 64 | captcha_id = 'id' | 64 | class FakedProxy(Recorder): |
2009 | 65 | captcha_solution = 'hello' | 65 | """A faked multiplatform proxy.""" |
2010 | 66 | self.root.register_user(app_name, email, password, name, captcha_id, | 66 | |
2011 | 67 | captcha_solution, | 67 | |
2012 | 68 | MATCH(callable), MATCH(callable)) | 68 | class FakedAccount(Recorder): |
2013 | 69 | self.mocker.replay() | 69 | """A faked Account processor.""" |
2014 | 70 | self.login.register_user(app_name, email, password, name, captcha_id, | 70 | |
2015 | 71 | captcha_solution) | 71 | |
2016 | 72 | 72 | class FakedCredentialsFactory(Recorder): | |
2017 | 73 | def test_login(self): | 73 | """A very dummy Credentials object.""" |
2018 | 74 | """Test that the call is relayed.""" | 74 | |
2019 | 75 | app_name = 'app' | 75 | credentials = FakedCredentials() |
2020 | 76 | email = 'email' | 76 | |
2021 | 77 | password = 'password' | 77 | # pylint: disable=C0103 |
2022 | 78 | self.root.login(app_name, email, password, | 78 | |
2023 | 79 | MATCH(callable), MATCH(callable), | 79 | def Credentials(self, *a, **kw): |
2024 | 80 | MATCH(callable)) | 80 | """Return always the same Credentials instance.""" |
2025 | 81 | self.mocker.mock() | 81 | return self.credentials |
2026 | 82 | self.mocker.replay() | 82 | |
2027 | 83 | self.login.login(app_name, email, password) | 83 | # pylint: enable=C0103 |
2028 | 84 | 84 | ||
2029 | 85 | def test_validate_email(self): | 85 | |
2030 | 86 | """Test that the call is relayed.""" | 86 | class TestExceptToErrdictException(Exception): |
2031 | 87 | app_name = 'app' | 87 | """A dummy exception for the following testcase.""" |
2032 | 88 | email = 'email' | 88 | |
2033 | 89 | password = 'passwrd' | 89 | |
2034 | 90 | email_token = 'token' | 90 | class ExceptToErrdictTestCase(TestCase): |
2035 | 91 | self.root.validate_email(app_name, email, password, email_token, | 91 | """Tests for the except_to_errdict function.""" |
2036 | 92 | MATCH(callable), | 92 | |
2037 | 93 | MATCH(callable)) | 93 | def test_first_arg_is_dict(self): |
2038 | 94 | self.mocker.replay() | 94 | """If the first arg is a dict, use it as the base dict.""" |
2039 | 95 | self.login.validate_email(app_name, email, password, email_token) | 95 | sample_dict = { |
2040 | 96 | 96 | "errorcode1": "error message 1", | |
2041 | 97 | def test_request_password_reset_token(self): | 97 | "errorcode2": "error message 2", |
2042 | 98 | """Test that the call is relayed.""" | 98 | "errorcode3": "error message 3", |
2043 | 99 | app_name = 'app' | 99 | } |
2044 | 100 | email = 'email' | 100 | e = TestExceptToErrdictException(sample_dict) |
2045 | 101 | self.root.request_password_reset_token(app_name, email, | 101 | result = except_to_errdict(e) |
2046 | 102 | MATCH(callable), | 102 | |
2047 | 103 | MATCH(callable)) | 103 | self.assertEqual(result["errtype"], e.__class__.__name__) |
2048 | 104 | self.mocker.replay() | 104 | for k in sample_dict.keys(): |
2049 | 105 | self.login.request_password_reset_token(app_name, email) | 105 | self.assertIn(k, result) |
2050 | 106 | 106 | self.assertEqual(result[k], sample_dict[k]) | |
2051 | 107 | def test_set_new_password(self): | 107 | |
2052 | 108 | """Test that the call is relayed.""" | 108 | def test_first_arg_is_str(self): |
2053 | 109 | app_name = 'app' | 109 | """If the first arg is a str, use it as the message.""" |
2054 | 110 | email = 'email' | 110 | sample_string = "a sample string" |
2055 | 111 | token = 'token' | 111 | e = TestExceptToErrdictException(sample_string) |
2056 | 112 | new_password = 'new' | 112 | result = except_to_errdict(e) |
2057 | 113 | self.root.set_new_password(app_name, email, token, new_password, | 113 | self.assertEqual(result["errtype"], e.__class__.__name__) |
2058 | 114 | MATCH(callable), | 114 | self.assertEqual(result["message"], sample_string) |
2059 | 115 | MATCH(callable)) | 115 | |
2060 | 116 | self.mocker.replay() | 116 | def test_first_arg_is_unicode(self): |
2061 | 117 | self.login.set_new_password(app_name, email, token, new_password) | 117 | """If the first arg is a unicode, use it as the message.""" |
2062 | 118 | 118 | sample_string = u"a sample string" | |
2063 | 119 | 119 | e = TestExceptToErrdictException(sample_string) | |
2064 | 120 | class CredentialsManagementMockedTestCase(MockerTestCase, TestCase): | 120 | result = except_to_errdict(e) |
2065 | 121 | """Test that the call are relied correctly.""" | 121 | self.assertEqual(result["errtype"], e.__class__.__name__) |
2066 | 122 | 122 | self.assertEqual(result["message"], sample_string) | |
2067 | 123 | def setUp(self): | 123 | |
2068 | 124 | """Setup tests.""" | 124 | def test_no_args_at_all(self): |
2069 | 125 | super(CredentialsManagementMockedTestCase, self).setUp() | 125 | """If there are no args, use the class docstring.""" |
2070 | 126 | self.root = self.mocker.mock() | 126 | e = TestExceptToErrdictException() |
2071 | 127 | self.cred = CredentialsManagement(None, None) | 127 | result = except_to_errdict(e) |
2072 | 128 | self.cred.root = self.root | 128 | self.assertEqual(result["errtype"], e.__class__.__name__) |
2073 | 129 | 129 | self.assertEqual(result["message"], e.__class__.__doc__) | |
2074 | 130 | |||
2075 | 131 | def test_some_other_thing_as_first_arg(self): | ||
2076 | 132 | """If first arg is not basestring nor dict, then repr all args.""" | ||
2077 | 133 | sample_args = (None, u"unicode2\ufffd", "errorcode3") | ||
2078 | 134 | e = TestExceptToErrdictException(*sample_args) | ||
2079 | 135 | result = except_to_errdict(e) | ||
2080 | 136 | self.assertEqual(result["errtype"], e.__class__.__name__) | ||
2081 | 137 | |||
2082 | 138 | |||
2083 | 139 | class BaseTestCase(TestCase): | ||
2084 | 140 | """Base test case.""" | ||
2085 | 141 | |||
2086 | 142 | timeout = 2 | ||
2087 | 143 | |||
2088 | 144 | @defer.inlineCallbacks | ||
2089 | 145 | def setUp(self): | ||
2090 | 146 | yield super(BaseTestCase, self).setUp() | ||
2091 | 147 | self.proxy = FakedProxy() | ||
2092 | 148 | |||
2093 | 149 | def assert_recorder_called(self, fake, method, *args, **kwargs): | ||
2094 | 150 | """Check that 'fake.method(*args, **kwargs)' was called.""" | ||
2095 | 151 | self.assertEqual(fake._called[method], [(args, kwargs)]) | ||
2096 | 152 | |||
2097 | 153 | |||
2098 | 154 | class SSOLoginTestCase(BaseTestCase): | ||
2099 | 155 | """Test the SSOLogin class.""" | ||
2100 | 156 | |||
2101 | 157 | @defer.inlineCallbacks | ||
2102 | 158 | def setUp(self): | ||
2103 | 159 | yield super(SSOLoginTestCase, self).setUp() | ||
2104 | 160 | |||
2105 | 161 | def ksc(keyring, k, val): | ||
2106 | 162 | """Assert over token and app_name.""" | ||
2107 | 163 | self.assertEqual(k, APP_NAME) | ||
2108 | 164 | self.assertEqual(val, TOKEN) | ||
2109 | 165 | self.keyring_was_set = True | ||
2110 | 166 | self.keyring_values = k, val | ||
2111 | 167 | return defer.succeed(None) | ||
2112 | 168 | |||
2113 | 169 | self.patch(main.Keyring, "set_credentials", ksc) | ||
2114 | 170 | self.patch(main, 'Account', FakedAccount) | ||
2115 | 171 | self.patch(main, "thread_execute", fake_ok_thread_execute) | ||
2116 | 172 | self.keyring_was_set = False | ||
2117 | 173 | self.keyring_values = None | ||
2118 | 174 | |||
2119 | 175 | self.obj = SSOLogin(self.proxy) | ||
2120 | 176 | |||
2121 | 177 | def test_creation(self): | ||
2122 | 178 | """Test that the object creation is successful.""" | ||
2123 | 179 | self.assertIsInstance(self.obj.processor, main.Account) | ||
2124 | 180 | self.assertTrue(self.obj.proxy is self.proxy) | ||
2125 | 181 | |||
2126 | 182 | @defer.inlineCallbacks | ||
2127 | 183 | def test_generate_captcha(self): | ||
2128 | 184 | """Test that the captcha method works ok.""" | ||
2129 | 185 | d = defer.Deferred() | ||
2130 | 186 | filename = "sample filename" | ||
2131 | 187 | expected_result = "expected result" | ||
2132 | 188 | |||
2133 | 189 | self.patch(self.obj, "CaptchaGenerated", lambda *a: d.callback(a)) | ||
2134 | 190 | self.patch(self.obj, "CaptchaGenerationError", d.errback) | ||
2135 | 191 | |||
2136 | 192 | self.obj.processor._next_result = expected_result | ||
2137 | 193 | self.obj.generate_captcha(APP_NAME, filename) | ||
2138 | 194 | |||
2139 | 195 | app_name, result = yield d | ||
2140 | 196 | self.assertEqual(result, expected_result) | ||
2141 | 197 | self.assertEqual(app_name, APP_NAME) | ||
2142 | 198 | self.assert_recorder_called(self.obj.processor, 'generate_captcha', | ||
2143 | 199 | filename) | ||
2144 | 200 | |||
2145 | 201 | @defer.inlineCallbacks | ||
2146 | 202 | def test_register_user(self): | ||
2147 | 203 | """Test that the register_user method works ok.""" | ||
2148 | 204 | d = defer.Deferred() | ||
2149 | 205 | expected_result = "expected result" | ||
2150 | 206 | |||
2151 | 207 | self.patch(self.obj, "UserRegistered", lambda *a: d.callback(a)) | ||
2152 | 208 | self.patch(self.obj, "UserRegistrationError", d.errback) | ||
2153 | 209 | |||
2154 | 210 | self.obj.processor._next_result = expected_result | ||
2155 | 211 | self.obj.register_user(APP_NAME, EMAIL, PASSWORD, NAME, CAPTCHA_ID, | ||
2156 | 212 | CAPTCHA_SOLUTION) | ||
2157 | 213 | |||
2158 | 214 | app_name, result = yield d | ||
2159 | 215 | self.assertEqual(result, expected_result) | ||
2160 | 216 | self.assertEqual(app_name, APP_NAME) | ||
2161 | 217 | self.assert_recorder_called(self.obj.processor, 'register_user', | ||
2162 | 218 | EMAIL, PASSWORD, NAME, CAPTCHA_ID, CAPTCHA_SOLUTION) | ||
2163 | 219 | |||
2164 | 220 | @defer.inlineCallbacks | ||
2165 | 221 | def test_login(self): | ||
2166 | 222 | """Test that the login method works ok.""" | ||
2167 | 223 | d = defer.Deferred() | ||
2168 | 224 | |||
2169 | 225 | self.patch(self.obj, "LoggedIn", lambda *a: d.callback(a)) | ||
2170 | 226 | self.patch(self.obj, "LoginError", d.errback) | ||
2171 | 227 | self.patch(self.obj, "UserNotValidated", d.errback) | ||
2172 | 228 | |||
2173 | 229 | self.obj.processor._next_result = TOKEN | ||
2174 | 230 | self.obj.login(APP_NAME, EMAIL, PASSWORD) | ||
2175 | 231 | |||
2176 | 232 | app_name, result = yield d | ||
2177 | 233 | self.assertEqual(result, EMAIL) | ||
2178 | 234 | self.assertEqual(app_name, APP_NAME) | ||
2179 | 235 | self.assertTrue(self.keyring_was_set, "The keyring should be set") | ||
2180 | 236 | self.assert_recorder_called(self.obj.processor, 'login', | ||
2181 | 237 | EMAIL, PASSWORD, main.get_token_name(APP_NAME)) | ||
2182 | 238 | |||
2183 | 239 | @defer.inlineCallbacks | ||
2184 | 240 | def test_validate_email(self): | ||
2185 | 241 | """Test that the validate_email method works ok.""" | ||
2186 | 242 | d = defer.Deferred() | ||
2187 | 243 | |||
2188 | 244 | self.patch(self.obj, "EmailValidated", lambda *a: d.callback(a)) | ||
2189 | 245 | self.patch(self.obj, "EmailValidationError", d.errback) | ||
2190 | 246 | |||
2191 | 247 | self.obj.processor._next_result = TOKEN | ||
2192 | 248 | self.obj.validate_email(APP_NAME, EMAIL, PASSWORD, EMAIL_TOKEN) | ||
2193 | 249 | |||
2194 | 250 | app_name, result = yield d | ||
2195 | 251 | self.assertEqual(result, EMAIL) | ||
2196 | 252 | self.assertEqual(app_name, APP_NAME) | ||
2197 | 253 | self.assertTrue(self.keyring_was_set, "The keyring should be set") | ||
2198 | 254 | self.assert_recorder_called(self.obj.processor, 'validate_email', | ||
2199 | 255 | EMAIL, PASSWORD, EMAIL_TOKEN, main.get_token_name(APP_NAME)) | ||
2200 | 256 | |||
2201 | 257 | @defer.inlineCallbacks | ||
2202 | 258 | def test_request_password_reset_token(self): | ||
2203 | 259 | """Test that the request_password_reset_token method works ok.""" | ||
2204 | 260 | d = defer.Deferred() | ||
2205 | 261 | |||
2206 | 262 | self.patch(self.obj, "PasswordResetTokenSent", | ||
2207 | 263 | lambda *a: d.callback(a)) | ||
2208 | 264 | self.patch(self.obj, "PasswordResetError", d.errback) | ||
2209 | 265 | |||
2210 | 266 | self.obj.processor._next_result = EMAIL | ||
2211 | 267 | self.obj.request_password_reset_token(APP_NAME, EMAIL) | ||
2212 | 268 | |||
2213 | 269 | app_name, result = yield d | ||
2214 | 270 | self.assertEqual(result, EMAIL) | ||
2215 | 271 | self.assertEqual(app_name, APP_NAME) | ||
2216 | 272 | self.assert_recorder_called(self.obj.processor, | ||
2217 | 273 | 'request_password_reset_token', EMAIL) | ||
2218 | 274 | |||
2219 | 275 | @defer.inlineCallbacks | ||
2220 | 276 | def test_set_new_password(self): | ||
2221 | 277 | """Test that the set_new_password method works ok.""" | ||
2222 | 278 | d = defer.Deferred() | ||
2223 | 279 | |||
2224 | 280 | self.patch(self.obj, "PasswordChanged", lambda *a: d.callback(a)) | ||
2225 | 281 | self.patch(self.obj, "PasswordChangeError", d.errback) | ||
2226 | 282 | |||
2227 | 283 | self.obj.processor._next_result = EMAIL | ||
2228 | 284 | self.obj.set_new_password(APP_NAME, EMAIL, EMAIL_TOKEN, PASSWORD) | ||
2229 | 285 | |||
2230 | 286 | app_name, result = yield d | ||
2231 | 287 | self.assertEqual(result, EMAIL) | ||
2232 | 288 | self.assertEqual(app_name, APP_NAME) | ||
2233 | 289 | self.assert_recorder_called(self.obj.processor, | ||
2234 | 290 | 'set_new_password', EMAIL, EMAIL_TOKEN, PASSWORD) | ||
2235 | 291 | |||
2236 | 292 | |||
2237 | 293 | class SSOLoginWithErrorTestCase(SSOLoginTestCase): | ||
2238 | 294 | """Test the SSOLogin class.""" | ||
2239 | 295 | |||
2240 | 296 | @defer.inlineCallbacks | ||
2241 | 297 | def setUp(self): | ||
2242 | 298 | yield super(SSOLoginWithErrorTestCase, self).setUp() | ||
2243 | 299 | self.patch(main, "thread_execute", fake_err_thread_execute) | ||
2244 | 300 | |||
2245 | 301 | @defer.inlineCallbacks | ||
2246 | 302 | def test_generate_captcha(self): | ||
2247 | 303 | """Test that the captcha method fails as expected.""" | ||
2248 | 304 | d = defer.Deferred() | ||
2249 | 305 | filename = "sample filename" | ||
2250 | 306 | |||
2251 | 307 | self.patch(self.obj, "CaptchaGenerated", d.errback) | ||
2252 | 308 | self.patch(self.obj, "CaptchaGenerationError", | ||
2253 | 309 | lambda *a: d.callback(a)) | ||
2254 | 310 | self.obj.generate_captcha(APP_NAME, filename) | ||
2255 | 311 | |||
2256 | 312 | app_name, errdict = yield d | ||
2257 | 313 | self.assertEqual(errdict["errtype"], "SampleException") | ||
2258 | 314 | self.assertEqual(app_name, APP_NAME) | ||
2259 | 315 | |||
2260 | 316 | @defer.inlineCallbacks | ||
2261 | 317 | def test_register_user(self): | ||
2262 | 318 | """Test that the register_user method fails as expected.""" | ||
2263 | 319 | d = defer.Deferred() | ||
2264 | 320 | |||
2265 | 321 | self.patch(self.obj, "UserRegistered", d.errback) | ||
2266 | 322 | self.patch(self.obj, "UserRegistrationError", lambda *a: d.callback(a)) | ||
2267 | 323 | self.obj.register_user(APP_NAME, EMAIL, PASSWORD, NAME, CAPTCHA_ID, | ||
2268 | 324 | CAPTCHA_SOLUTION) | ||
2269 | 325 | |||
2270 | 326 | app_name, errdict = yield d | ||
2271 | 327 | self.assertEqual(errdict["errtype"], "SampleException") | ||
2272 | 328 | self.assertEqual(app_name, APP_NAME) | ||
2273 | 329 | |||
2274 | 330 | @defer.inlineCallbacks | ||
2275 | 331 | def test_login(self): | ||
2276 | 332 | """The login method fails as expected when get_token_name fails.""" | ||
2277 | 333 | d = defer.Deferred() | ||
2278 | 334 | |||
2279 | 335 | def fake_gtn(*args): | ||
2280 | 336 | """A fake get_token_name that fails.""" | ||
2281 | 337 | raise SampleException() | ||
2282 | 338 | |||
2283 | 339 | self.patch(main, "get_token_name", fake_gtn) | ||
2284 | 340 | self.patch(self.obj, "LoggedIn", d.errback) | ||
2285 | 341 | self.patch(self.obj, "LoginError", lambda *a: d.callback(a)) | ||
2286 | 342 | self.patch(self.obj, "UserNotValidated", d.errback) | ||
2287 | 343 | self.obj.login(APP_NAME, EMAIL, PASSWORD) | ||
2288 | 344 | |||
2289 | 345 | app_name, errdict = yield d | ||
2290 | 346 | self.assertEqual(app_name, APP_NAME) | ||
2291 | 347 | self.assertEqual(errdict["errtype"], "SampleException") | ||
2292 | 348 | self.assertFalse(self.keyring_was_set, "Keyring should not be set") | ||
2293 | 349 | |||
2294 | 350 | @defer.inlineCallbacks | ||
2295 | 351 | def test_login_set_credentials(self): | ||
2296 | 352 | """The login method fails as expected when set_credentials fails.""" | ||
2297 | 353 | d = defer.Deferred() | ||
2298 | 354 | |||
2299 | 355 | def fake_set_creds(*args): | ||
2300 | 356 | """A fake Keyring.set_credentials that fails.""" | ||
2301 | 357 | return defer.fail(SampleException()) | ||
2302 | 358 | |||
2303 | 359 | self.patch(main.Keyring, "set_credentials", fake_set_creds) | ||
2304 | 360 | fail = lambda app, res: d.errback((app, res)) | ||
2305 | 361 | self.patch(self.obj, "LoggedIn", fail) | ||
2306 | 362 | self.patch(self.obj, "LoginError", lambda *a: d.callback(a)) | ||
2307 | 363 | self.patch(self.obj, "UserNotValidated", fail) | ||
2308 | 364 | self.obj.login(APP_NAME, EMAIL, PASSWORD) | ||
2309 | 365 | |||
2310 | 366 | app_name, errdict = yield d | ||
2311 | 367 | self.assertEqual(app_name, APP_NAME) | ||
2312 | 368 | self.assertEqual(errdict["errtype"], "SampleException") | ||
2313 | 369 | self.assertFalse(self.keyring_was_set, "Keyring should not be set") | ||
2314 | 370 | |||
2315 | 371 | @defer.inlineCallbacks | ||
2316 | 372 | def test_validate_email(self): | ||
2317 | 373 | """Test that the validate_email method fails as expected.""" | ||
2318 | 374 | d = defer.Deferred() | ||
2319 | 375 | |||
2320 | 376 | def fake_gtn(*args): | ||
2321 | 377 | """A fake get_token_name that fails.""" | ||
2322 | 378 | raise SampleException() | ||
2323 | 379 | |||
2324 | 380 | self.patch(main, "get_token_name", fake_gtn) | ||
2325 | 381 | |||
2326 | 382 | self.patch(self.obj, "EmailValidated", d.errback) | ||
2327 | 383 | self.patch(self.obj, "EmailValidationError", lambda *a: d.callback(a)) | ||
2328 | 384 | self.obj.validate_email(APP_NAME, EMAIL, PASSWORD, EMAIL_TOKEN) | ||
2329 | 385 | |||
2330 | 386 | app_name, errdict = yield d | ||
2331 | 387 | self.assertEqual(app_name, APP_NAME) | ||
2332 | 388 | self.assertEqual(errdict["errtype"], "SampleException") | ||
2333 | 389 | self.assertFalse(self.keyring_was_set, "Keyring should not be set") | ||
2334 | 390 | |||
2335 | 391 | @defer.inlineCallbacks | ||
2336 | 392 | def test_request_password_reset_token(self): | ||
2337 | 393 | """Test the request_password_reset_token method fails as expected.""" | ||
2338 | 394 | d = defer.Deferred() | ||
2339 | 395 | |||
2340 | 396 | self.patch(self.obj, "PasswordResetTokenSent", d.errback) | ||
2341 | 397 | self.patch(self.obj, "PasswordResetError", lambda *a: d.callback(a)) | ||
2342 | 398 | self.obj.request_password_reset_token(APP_NAME, EMAIL) | ||
2343 | 399 | |||
2344 | 400 | app_name, errdict = yield d | ||
2345 | 401 | self.assertEqual(errdict["errtype"], "SampleException") | ||
2346 | 402 | self.assertEqual(app_name, APP_NAME) | ||
2347 | 403 | |||
2348 | 404 | @defer.inlineCallbacks | ||
2349 | 405 | def test_set_new_password(self): | ||
2350 | 406 | """Test that the set_new_password method fails as expected.""" | ||
2351 | 407 | d = defer.Deferred() | ||
2352 | 408 | |||
2353 | 409 | self.patch(self.obj, "PasswordChanged", d.errback) | ||
2354 | 410 | self.patch(self.obj, "PasswordChangeError", lambda *a: d.callback(a)) | ||
2355 | 411 | self.obj.set_new_password(APP_NAME, EMAIL, EMAIL_TOKEN, PASSWORD) | ||
2356 | 412 | |||
2357 | 413 | app_name, errdict = yield d | ||
2358 | 414 | self.assertEqual(errdict["errtype"], "SampleException") | ||
2359 | 415 | self.assertEqual(app_name, APP_NAME) | ||
2360 | 416 | |||
2361 | 417 | |||
2362 | 418 | class BlockingFunctionTestCase(TestCase): | ||
2363 | 419 | """Tests for the "thread_execute" function.""" | ||
2364 | 420 | |||
2365 | 421 | timeout = 5 | ||
2366 | 422 | |||
2367 | 423 | @defer.inlineCallbacks | ||
2368 | 424 | def test_thread_execute(self): | ||
2369 | 425 | """Test the normal behaviour.""" | ||
2370 | 426 | d = defer.Deferred() | ||
2371 | 427 | expected_result = "expected result" | ||
2372 | 428 | |||
2373 | 429 | def f(): | ||
2374 | 430 | """No failure.""" | ||
2375 | 431 | return expected_result | ||
2376 | 432 | |||
2377 | 433 | thread_execute(f, APP_NAME, lambda *a: d.callback(a), d.errback) | ||
2378 | 434 | |||
2379 | 435 | app_name, result = yield d | ||
2380 | 436 | |||
2381 | 437 | self.assertEqual(result, expected_result) | ||
2382 | 438 | self.assertEqual(app_name, APP_NAME) | ||
2383 | 439 | |||
2384 | 440 | @defer.inlineCallbacks | ||
2385 | 441 | def test_thread_execute_error(self): | ||
2386 | 442 | """Test the behaviour when an Exception is raised.""" | ||
2387 | 443 | d = defer.Deferred() | ||
2388 | 444 | expected_error_message = "expected error message" | ||
2389 | 445 | expected_exc = SampleException(expected_error_message) | ||
2390 | 446 | |||
2391 | 447 | def f(): | ||
2392 | 448 | """Failure.""" | ||
2393 | 449 | raise expected_exc | ||
2394 | 450 | |||
2395 | 451 | thread_execute(f, APP_NAME, d.errback, lambda *a: d.callback(a)) | ||
2396 | 452 | |||
2397 | 453 | app_name, error = yield d | ||
2398 | 454 | |||
2399 | 455 | self.assertEqual(app_name, APP_NAME) | ||
2400 | 456 | self.assertEqual(error, expected_exc) | ||
2401 | 457 | |||
2402 | 458 | |||
2403 | 459 | class CredentialsManagementTestCase(BaseTestCase): | ||
2404 | 460 | """Tests for the CredentialsManagement DBus interface.""" | ||
2405 | 461 | |||
2406 | 462 | base_args = { | ||
2407 | 463 | HELP_TEXT_KEY: HELP_TEXT, PING_URL_KEY: PING_URL, | ||
2408 | 464 | TC_URL_KEY: TC_URL, WINDOW_ID_KEY: WINDOW_ID, | ||
2409 | 465 | UI_CLASS_KEY: 'SuperUI', UI_MODULE_KEY: 'foo.bar.baz', | ||
2410 | 466 | } | ||
2411 | 467 | |||
2412 | 468 | @defer.inlineCallbacks | ||
2413 | 469 | def setUp(self): | ||
2414 | 470 | yield super(CredentialsManagementTestCase, self).setUp() | ||
2415 | 471 | |||
2416 | 472 | self.factory = FakedCredentialsFactory() | ||
2417 | 473 | self.patch(main, 'Credentials', self.factory.Credentials) | ||
2418 | 474 | self.obj = CredentialsManagement(timeout_func=lambda *a: None, | ||
2419 | 475 | shutdown_func=lambda *a: None, | ||
2420 | 476 | proxy=self.proxy) | ||
2421 | 477 | self.args = {} | ||
2422 | 478 | self.cred_args = {} | ||
2423 | 479 | |||
2424 | 480 | self.memento = MementoHandler() | ||
2425 | 481 | self.memento.setLevel(logging.DEBUG) | ||
2426 | 482 | main.logger.addHandler(self.memento) | ||
2427 | 483 | self.addCleanup(main.logger.removeHandler, self.memento) | ||
2428 | 484 | |||
2429 | 485 | |||
2430 | 486 | class CredentialsManagementRefCountingTestCase(CredentialsManagementTestCase): | ||
2431 | 487 | """Tests for the CredentialsManagement ref counting.""" | ||
2432 | 488 | |||
2433 | 489 | @defer.inlineCallbacks | ||
2434 | 490 | def assert_refcounter_increased(self, method_name, signal_name=None): | ||
2435 | 491 | """Assert that calling 'method_name' increases the ref counter.""" | ||
2436 | 492 | if signal_name is not None: | ||
2437 | 493 | d = defer.Deferred() | ||
2438 | 494 | self.patch(self.obj, signal_name, lambda *a: d.callback(a)) | ||
2439 | 495 | else: | ||
2440 | 496 | d = defer.succeed(None) | ||
2441 | 497 | |||
2442 | 498 | getattr(self.obj, method_name)(APP_NAME, self.args) | ||
2443 | 499 | |||
2444 | 500 | yield d | ||
2445 | 501 | self.assertEqual(self.obj.ref_count, 1) | ||
2446 | 502 | |||
2447 | 503 | def assert_refcounter_decreased(self, signal_name, *a, **kw): | ||
2448 | 504 | """Assert that calling 'signal_name' decreases the ref counter.""" | ||
2449 | 505 | self.obj.ref_count = 3 | ||
2450 | 506 | |||
2451 | 507 | getattr(self.obj, signal_name)(*a, **kw) | ||
2452 | 508 | |||
2453 | 509 | self.assertEqual(self.obj.ref_count, 2) | ||
2454 | 510 | |||
2455 | 511 | def assert_refcounter_negative(self, signal_name, *a, **kw): | ||
2456 | 512 | """Assert that calling 'signal_name' decreases the ref counter. | ||
2457 | 513 | |||
2458 | 514 | If decreased value is negative, assert that a warning log was made. | ||
2459 | 515 | |||
2460 | 516 | """ | ||
2461 | 517 | self.obj._ref_count = -3 # overwrite internal to force a negative | ||
2462 | 518 | |||
2463 | 519 | getattr(self.obj, signal_name)(*a, **kw) | ||
2464 | 520 | |||
2465 | 521 | self.assertEqual(self.obj.ref_count, 0) | ||
2466 | 522 | msg = 'Attempting to decrease ref_count to a negative value (-4).' | ||
2467 | 523 | self.assertTrue(self.memento.check_warning(msg)) | ||
2468 | 524 | |||
2469 | 525 | def test_ref_counting(self): | ||
2470 | 526 | """Ref counting is in place.""" | ||
2471 | 527 | self.assertEqual(self.obj.ref_count, 0) | ||
2472 | 528 | |||
2473 | 529 | @defer.inlineCallbacks | ||
2474 | 130 | def test_find_credentials(self): | 530 | def test_find_credentials(self): |
2483 | 131 | """Test that the call is relayed.""" | 531 | """Keep proper track of ongoing requests.""" |
2484 | 132 | app_name = 'app' | 532 | yield self.assert_refcounter_increased('find_credentials', |
2485 | 133 | args = 'args' | 533 | 'CredentialsFound') |
2486 | 134 | self.root.find_credentials(app_name, args, MATCH(callable), | 534 | |
2487 | 135 | MATCH(callable)) | 535 | @defer.inlineCallbacks |
2488 | 136 | self.mocker.replay() | 536 | def test_find_credentials_with_success_cb(self): |
2489 | 137 | self.cred.find_credentials(app_name, args) | 537 | """Keep proper track of ongoing requests.""" |
2490 | 138 | 538 | d = defer.Deferred() | |
2491 | 539 | counter = [] | ||
2492 | 540 | |||
2493 | 541 | def in_the_middle(): | ||
2494 | 542 | """Store the ref_count value.""" | ||
2495 | 543 | counter.append(self.obj.ref_count) | ||
2496 | 544 | return defer.succeed(TOKEN) | ||
2497 | 545 | |||
2498 | 546 | self.patch(self.factory.credentials, 'find_credentials', in_the_middle) | ||
2499 | 547 | self.obj.find_credentials(APP_NAME, self.args, | ||
2500 | 548 | success_cb=d.callback, error_cb=d.errback) | ||
2501 | 549 | yield d | ||
2502 | 550 | self.assertEqual(counter, [1]) | ||
2503 | 551 | self.assertEqual(self.obj.ref_count, 0) | ||
2504 | 552 | |||
2505 | 553 | @defer.inlineCallbacks | ||
2506 | 554 | def test_find_credentials_with_error_cb(self): | ||
2507 | 555 | """Keep proper track of ongoing requests.""" | ||
2508 | 556 | d = defer.Deferred() | ||
2509 | 557 | counter = [] | ||
2510 | 558 | |||
2511 | 559 | def in_the_middle(): | ||
2512 | 560 | """Store the ref_count value.""" | ||
2513 | 561 | counter.append(self.obj.ref_count) | ||
2514 | 562 | return defer.fail('foo') | ||
2515 | 563 | |||
2516 | 564 | self.patch(self.factory.credentials, 'find_credentials', in_the_middle) | ||
2517 | 565 | self.obj.find_credentials(APP_NAME, self.args, | ||
2518 | 566 | success_cb=d.errback, error_cb=d.callback) | ||
2519 | 567 | |||
2520 | 568 | yield d | ||
2521 | 569 | self.assertEqual(counter, [1]) | ||
2522 | 570 | self.assertEqual(self.obj.ref_count, 0) | ||
2523 | 571 | |||
2524 | 572 | @defer.inlineCallbacks | ||
2525 | 139 | def test_clear_credentials(self): | 573 | def test_clear_credentials(self): |
2533 | 140 | """Test that the call is relayed.""" | 574 | """Keep proper track of ongoing requests.""" |
2534 | 141 | app_name = 'app' | 575 | yield self.assert_refcounter_increased('clear_credentials', |
2535 | 142 | args = 'args' | 576 | 'CredentialsCleared') |
2529 | 143 | self.root.clear_credentials(app_name, args, MATCH(callable), | ||
2530 | 144 | MATCH(callable)) | ||
2531 | 145 | self.mocker.replay() | ||
2532 | 146 | self.cred.clear_credentials(app_name, args) | ||
2536 | 147 | 577 | ||
2537 | 578 | @defer.inlineCallbacks | ||
2538 | 148 | def test_store_credentials(self): | 579 | def test_store_credentials(self): |
2562 | 149 | """Test that the call is relayed.""" | 580 | """Keep proper track of ongoing requests.""" |
2563 | 150 | app_name = 'app' | 581 | yield self.assert_refcounter_increased('store_credentials', |
2564 | 151 | args = 'args' | 582 | 'CredentialsStored') |
2565 | 152 | self.root.store_credentials(app_name, args, MATCH(callable), | 583 | |
2566 | 153 | MATCH(callable)) | 584 | def test_register(self): |
2567 | 154 | self.mocker.replay() | 585 | """Keep proper track of ongoing requests.""" |
2568 | 155 | self.cred.store_credentials(app_name, args) | 586 | yield self.assert_refcounter_increased('register') |
2569 | 156 | 587 | ||
2570 | 157 | def test_register(self): | 588 | def test_login(self): |
2571 | 158 | """Test that the call is relayed.""" | 589 | """Keep proper track of ongoing requests.""" |
2572 | 159 | app_name = 'app' | 590 | yield self.assert_refcounter_increased('login') |
2573 | 160 | args = 'args' | 591 | |
2574 | 161 | self.root.register(app_name, args) | 592 | def test_several_requests(self): |
2575 | 162 | self.mocker.replay() | 593 | """Requests can be nested.""" |
2576 | 163 | self.cred.register(app_name, args) | 594 | self.obj.login(APP_NAME, self.args) |
2577 | 164 | 595 | self.obj.register(APP_NAME, self.args) | |
2578 | 165 | def test_login(self): | 596 | self.obj.login(APP_NAME, self.args) |
2579 | 166 | """Test that the call is relayed.""" | 597 | self.obj.register(APP_NAME, self.args) |
2580 | 167 | app_name = 'app' | 598 | self.obj.register(APP_NAME, self.args) |
2581 | 168 | args = 'args' | 599 | |
2582 | 169 | self.root.login(app_name, args) | 600 | self.assertEqual(self.obj.ref_count, 5) |
2583 | 170 | self.mocker.replay() | 601 | |
2584 | 171 | self.cred.login(app_name, args) | 602 | def test_credentials_found(self): |
2585 | 603 | """Ref counter is decreased when a signal is sent.""" | ||
2586 | 604 | self.assert_refcounter_decreased('CredentialsFound', APP_NAME, TOKEN) | ||
2587 | 605 | |||
2588 | 606 | def test_credentials_not_found(self): | ||
2589 | 607 | """Ref counter is decreased when a signal is sent.""" | ||
2590 | 608 | self.assert_refcounter_decreased('CredentialsNotFound', APP_NAME) | ||
2591 | 609 | |||
2592 | 610 | def test_credentials_cleared(self): | ||
2593 | 611 | """Ref counter is decreased when a signal is sent.""" | ||
2594 | 612 | self.assert_refcounter_decreased('CredentialsCleared', APP_NAME) | ||
2595 | 613 | |||
2596 | 614 | def test_credentials_stored(self): | ||
2597 | 615 | """Ref counter is decreased when a signal is sent.""" | ||
2598 | 616 | self.assert_refcounter_decreased('CredentialsStored', APP_NAME) | ||
2599 | 617 | |||
2600 | 618 | def test_credentials_error(self): | ||
2601 | 619 | """Ref counter is decreased when a signal is sent.""" | ||
2602 | 620 | self.assert_refcounter_decreased('CredentialsError', APP_NAME, | ||
2603 | 621 | SampleException('test')) | ||
2604 | 622 | |||
2605 | 623 | def test_authorization_denied(self): | ||
2606 | 624 | """Ref counter is decreased when a signal is sent.""" | ||
2607 | 625 | self.assert_refcounter_decreased('AuthorizationDenied', APP_NAME) | ||
2608 | 626 | |||
2609 | 627 | def test_credentials_found_when_ref_count_is_not_positive(self): | ||
2610 | 628 | """Ref counter is decreased when a signal is sent.""" | ||
2611 | 629 | self.assert_refcounter_negative('CredentialsFound', APP_NAME, TOKEN) | ||
2612 | 630 | |||
2613 | 631 | def test_credentials_not_found_when_ref_count_is_not_positive(self): | ||
2614 | 632 | """Ref counter is decreased when a signal is sent.""" | ||
2615 | 633 | self.assert_refcounter_negative('CredentialsNotFound', APP_NAME) | ||
2616 | 634 | |||
2617 | 635 | def test_credentials_cleared_when_ref_count_is_not_positive(self): | ||
2618 | 636 | """Ref counter is decreased when a signal is sent.""" | ||
2619 | 637 | self.assert_refcounter_negative('CredentialsCleared', APP_NAME) | ||
2620 | 638 | |||
2621 | 639 | def test_credentials_stored_when_ref_count_is_not_positive(self): | ||
2622 | 640 | """Ref counter is decreased when a signal is sent.""" | ||
2623 | 641 | self.assert_refcounter_negative('CredentialsStored', APP_NAME) | ||
2624 | 642 | |||
2625 | 643 | def test_credentials_error_when_ref_count_is_not_positive(self): | ||
2626 | 644 | """Ref counter is decreased when a signal is sent.""" | ||
2627 | 645 | self.assert_refcounter_negative('CredentialsError', APP_NAME, | ||
2628 | 646 | SampleException('test')) | ||
2629 | 647 | |||
2630 | 648 | def test_autorization_denied_when_ref_count_is_not_positive(self): | ||
2631 | 649 | """Ref counter is decreased when a signal is sent.""" | ||
2632 | 650 | self.assert_refcounter_negative('AuthorizationDenied', APP_NAME) | ||
2633 | 651 | |||
2634 | 652 | def test_on_zero_ref_count_shutdown(self): | ||
2635 | 653 | """When ref count reaches 0, queue shutdown op.""" | ||
2636 | 654 | self.patch(self.obj, 'timeout_func', self._set_called) | ||
2637 | 655 | self.obj.login(APP_NAME, self.args) | ||
2638 | 656 | self.obj.CredentialsFound(APP_NAME, TOKEN) | ||
2639 | 657 | |||
2640 | 658 | self.assertEqual(self._called, | ||
2641 | 659 | ((TIMEOUT_INTERVAL, self.obj.shutdown), {})) | ||
2642 | 660 | |||
2643 | 661 | def test_on_non_zero_ref_count_do_not_shutdown(self): | ||
2644 | 662 | """If ref count is not 0, do not queue shutdown op.""" | ||
2645 | 663 | self.patch(self.obj, 'timeout_func', self._set_called) | ||
2646 | 664 | self.obj.login(APP_NAME, self.args) | ||
2647 | 665 | |||
2648 | 666 | self.assertEqual(self._called, False) | ||
2649 | 667 | |||
2650 | 668 | def test_on_non_zero_ref_count_after_zero_do_not_shutdown(self): | ||
2651 | 669 | """If the shutdown was queued, do not quit if counter is not zero.""" | ||
2652 | 670 | |||
2653 | 671 | def fake_timeout_func(interval, func): | ||
2654 | 672 | """Start a new request when the timer is started.""" | ||
2655 | 673 | self.obj.register(APP_NAME, self.args) | ||
2656 | 674 | assert self.obj.ref_count > 0 | ||
2657 | 675 | func() | ||
2658 | 676 | |||
2659 | 677 | self.patch(self.obj, 'timeout_func', fake_timeout_func) | ||
2660 | 678 | self.patch(self.obj, 'shutdown_func', self._set_called) | ||
2661 | 679 | |||
2662 | 680 | self.obj.login(APP_NAME, self.args) | ||
2663 | 681 | self.obj.CredentialsFound(APP_NAME, TOKEN) | ||
2664 | 682 | # counter reached 0, timeout_func was called | ||
2665 | 683 | |||
2666 | 684 | self.assertEqual(self._called, False, 'shutdown_func was not called') | ||
2667 | 685 | |||
2668 | 686 | def test_zero_ref_count_after_zero_do_shutdown(self): | ||
2669 | 687 | """If the shutdown was queued, do quit if counter is zero.""" | ||
2670 | 688 | |||
2671 | 689 | def fake_timeout_func(interval, func): | ||
2672 | 690 | """Start a new request when the timer is started.""" | ||
2673 | 691 | assert self.obj.ref_count == 0 | ||
2674 | 692 | func() | ||
2675 | 693 | |||
2676 | 694 | self.patch(self.obj, 'timeout_func', fake_timeout_func) | ||
2677 | 695 | self.patch(self.obj, 'shutdown_func', self._set_called) | ||
2678 | 696 | |||
2679 | 697 | self.obj.login(APP_NAME, self.args) | ||
2680 | 698 | self.obj.CredentialsFound(APP_NAME, TOKEN) | ||
2681 | 699 | # counter reached 0, timeout_func was called | ||
2682 | 700 | |||
2683 | 701 | self.assertEqual(self._called, ((), {}), 'shutdown_func was called') | ||
2684 | 702 | |||
2685 | 703 | |||
2686 | 704 | class CredentialsManagementClearTestCase(CredentialsManagementTestCase): | ||
2687 | 705 | """Tests for the CredentialsManagement clear method.""" | ||
2688 | 706 | |||
2689 | 707 | method = 'clear_credentials' | ||
2690 | 708 | success_signal = 'CredentialsCleared' | ||
2691 | 709 | success_result = (APP_NAME,) | ||
2692 | 710 | others_should_errback = [] | ||
2693 | 711 | |||
2694 | 712 | @defer.inlineCallbacks | ||
2695 | 713 | def setUp(self): | ||
2696 | 714 | yield super(CredentialsManagementClearTestCase, self).setUp() | ||
2697 | 715 | self.call_method = getattr(self.obj, self.method) | ||
2698 | 716 | |||
2699 | 717 | def test_backend_called(self): | ||
2700 | 718 | """The credentials backend is properly called.""" | ||
2701 | 719 | self.call_method(APP_NAME, self.args) | ||
2702 | 720 | self.assert_recorder_called(self.factory, 'Credentials', APP_NAME) | ||
2703 | 721 | |||
2704 | 722 | @defer.inlineCallbacks | ||
2705 | 723 | def test_does_not_block(self): | ||
2706 | 724 | """Calling 'method' does not block but return thru signals.""" | ||
2707 | 725 | d = defer.Deferred() | ||
2708 | 726 | |||
2709 | 727 | self.patch(self.obj, self.success_signal, lambda *a: d.callback(a)) | ||
2710 | 728 | self.patch(self.obj, 'CredentialsError', | ||
2711 | 729 | lambda *a: d.errback(SampleException(a))) | ||
2712 | 730 | for signal in self.others_should_errback: | ||
2713 | 731 | self.patch(self.obj, signal, | ||
2714 | 732 | lambda *a: d.errback(AssertionError(a))) | ||
2715 | 733 | |||
2716 | 734 | self.call_method(APP_NAME, self.args) | ||
2717 | 735 | |||
2718 | 736 | result = yield d | ||
2719 | 737 | self.assertEqual(result, self.success_result) | ||
2720 | 738 | |||
2721 | 739 | @defer.inlineCallbacks | ||
2722 | 740 | def test_handles_error(self): | ||
2723 | 741 | """If calling the backend fails, CredentialsError is sent.""" | ||
2724 | 742 | d = defer.Deferred() | ||
2725 | 743 | |||
2726 | 744 | self.patch(self.obj, self.success_signal, d.errback) | ||
2727 | 745 | self.patch(self.obj, 'CredentialsError', lambda *a: d.callback(a)) | ||
2728 | 746 | |||
2729 | 747 | exc = SampleException('baz') | ||
2730 | 748 | self.patch(self.factory.credentials, self.method, | ||
2731 | 749 | lambda *a: defer.fail(exc)) | ||
2732 | 750 | self.call_method(APP_NAME, self.args) | ||
2733 | 751 | |||
2734 | 752 | app_name, error = yield d | ||
2735 | 753 | self.assertEqual(error, exc) | ||
2736 | 754 | self.assertEqual(app_name, APP_NAME) | ||
2737 | 755 | |||
2738 | 756 | |||
2739 | 757 | class CredentialsManagementStoreTestCase(CredentialsManagementClearTestCase): | ||
2740 | 758 | """Tests for the CredentialsManagement store method.""" | ||
2741 | 759 | |||
2742 | 760 | method = 'store_credentials' | ||
2743 | 761 | success_signal = 'CredentialsStored' | ||
2744 | 762 | |||
2745 | 763 | |||
2746 | 764 | class CredentialsManagementFindTestCase(CredentialsManagementClearTestCase): | ||
2747 | 765 | """Tests for the CredentialsManagement find method.""" | ||
2748 | 766 | |||
2749 | 767 | method = 'find_credentials' | ||
2750 | 768 | success_signal = 'CredentialsFound' | ||
2751 | 769 | success_result = (APP_NAME, TOKEN) | ||
2752 | 770 | others_should_errback = ['CredentialsNotFound'] | ||
2753 | 771 | |||
2754 | 772 | @defer.inlineCallbacks | ||
2755 | 773 | def test_find_credentials_with_success_cb(self): | ||
2756 | 774 | """The credentials are asked and returned in a thread_execute call.""" | ||
2757 | 775 | d = defer.Deferred() | ||
2758 | 776 | |||
2759 | 777 | self.obj.find_credentials(APP_NAME, self.args, | ||
2760 | 778 | success_cb=d.callback, error_cb=d.errback) | ||
2761 | 779 | |||
2762 | 780 | creds = yield d | ||
2763 | 781 | expected = yield self.factory.credentials.find_credentials() | ||
2764 | 782 | self.assertEqual(creds, expected) | ||
2765 | 783 | self.assert_recorder_called(self.factory, 'Credentials', APP_NAME) | ||
2766 | 784 | |||
2767 | 785 | @defer.inlineCallbacks | ||
2768 | 786 | def test_find_credentials_with_error_cb(self): | ||
2769 | 787 | """If find_credentials fails, error_cb is called.""" | ||
2770 | 788 | d = defer.Deferred() | ||
2771 | 789 | |||
2772 | 790 | self.patch(self.factory.credentials, 'find_credentials', | ||
2773 | 791 | lambda *a: defer.fail(SampleException('baz'))) | ||
2774 | 792 | self.obj.find_credentials(APP_NAME, self.args, | ||
2775 | 793 | success_cb=d.errback, error_cb=d.callback) | ||
2776 | 794 | |||
2777 | 795 | errdict = yield d | ||
2778 | 796 | self.assertEqual(errdict["errtype"], "SampleException") | ||
2779 | 797 | self.assert_recorder_called(self.factory, 'Credentials', APP_NAME) | ||
2780 | 798 | |||
2781 | 799 | |||
2782 | 800 | class CredentialsManagementNotFindTestCase(CredentialsManagementFindTestCase): | ||
2783 | 801 | """Tests for the CredentialsManagement find method.""" | ||
2784 | 802 | |||
2785 | 803 | success_signal = 'CredentialsNotFound' | ||
2786 | 804 | success_result = (APP_NAME,) | ||
2787 | 805 | others_should_errback = ['CredentialsFound'] | ||
2788 | 806 | |||
2789 | 807 | @defer.inlineCallbacks | ||
2790 | 808 | def setUp(self): | ||
2791 | 809 | yield super(CredentialsManagementNotFindTestCase, self).setUp() | ||
2792 | 810 | self.call_method = getattr(self.obj, self.method) | ||
2793 | 811 | self.patch(self.factory.credentials, self.method, | ||
2794 | 812 | lambda: defer.succeed({})) | ||
2795 | 813 | |||
2796 | 814 | |||
2797 | 815 | class CredentialsManagementOpsTestCase(CredentialsManagementTestCase): | ||
2798 | 816 | """Tests for the CredentialsManagement login/register methods.""" | ||
2799 | 817 | |||
2800 | 818 | @defer.inlineCallbacks | ||
2801 | 819 | def setUp(self): | ||
2802 | 820 | yield super(CredentialsManagementOpsTestCase, self).setUp() | ||
2803 | 821 | self.args = dict((k, str(v)) for k, v in self.base_args.iteritems()) | ||
2804 | 822 | self.cred_args = self.base_args.copy() | ||
2805 | 823 | self.cred_args[SUCCESS_CB_KEY] = self.obj.CredentialsFound | ||
2806 | 824 | self.cred_args[ERROR_CB_KEY] = self.obj.CredentialsError | ||
2807 | 825 | self.cred_args[DENIAL_CB_KEY] = self.obj.AuthorizationDenied | ||
2808 | 826 | |||
2809 | 827 | def test_register(self): | ||
2810 | 828 | """The registration is correct.""" | ||
2811 | 829 | self.obj.register(APP_NAME, self.args) | ||
2812 | 830 | self.assert_recorder_called(self.factory, 'Credentials', | ||
2813 | 831 | APP_NAME, **self.cred_args) | ||
2814 | 832 | |||
2815 | 833 | def test_login(self): | ||
2816 | 834 | """The login is correct.""" | ||
2817 | 835 | self.obj.login(APP_NAME, self.args) | ||
2818 | 836 | self.assert_recorder_called(self.factory, 'Credentials', | ||
2819 | 837 | APP_NAME, **self.cred_args) | ||
2820 | 172 | 838 | ||
2821 | 173 | def test_login_email_password(self): | 839 | def test_login_email_password(self): |
2828 | 174 | """Test that the call is relayed.""" | 840 | """The login_email_password is correct.""" |
2829 | 175 | app_name = 'app' | 841 | self.args['email'] = EMAIL |
2830 | 176 | args = 'args' | 842 | self.args['password'] = PASSWORD |
2831 | 177 | self.root.login_email_password(app_name, args) | 843 | self.obj.login_email_password(APP_NAME, self.args) |
2832 | 178 | self.mocker.replay() | 844 | self.assert_recorder_called(self.factory, 'Credentials', |
2833 | 179 | self.cred.login_email_password(app_name, args) | 845 | APP_NAME, **self.cred_args) |
2834 | 846 | |||
2835 | 847 | |||
2836 | 848 | class CredentialsManagementParamsTestCase(CredentialsManagementOpsTestCase): | ||
2837 | 849 | """Tests for the CredentialsManagement extra parameters handling.""" | ||
2838 | 850 | |||
2839 | 851 | @defer.inlineCallbacks | ||
2840 | 852 | def setUp(self): | ||
2841 | 853 | yield super(CredentialsManagementParamsTestCase, self).setUp() | ||
2842 | 854 | self.args['dummy'] = 'nothing useful' | ||
2843 | 855 | |||
2844 | 856 | |||
2845 | 857 | class CredentialsManagementSignalsTestCase(CredentialsManagementTestCase): | ||
2846 | 858 | """Tests for the CredentialsManagement DBus signals.""" | ||
2847 | 859 | |||
2848 | 860 | def test_credentials_found(self): | ||
2849 | 861 | """The CredentialsFound signal.""" | ||
2850 | 862 | self.obj.CredentialsFound(APP_NAME, TOKEN) | ||
2851 | 863 | msgs = (self.obj.__class__.__name__, | ||
2852 | 864 | self.obj.CredentialsFound.__name__, APP_NAME) | ||
2853 | 865 | self.assertTrue(self.memento.check_info(*msgs)) | ||
2854 | 866 | |||
2855 | 867 | msg = 'credentials must not be logged (found %r in log).' | ||
2856 | 868 | for val in TOKEN.itervalues(): | ||
2857 | 869 | self.assertFalse(self.memento.check_info(val), msg % val) | ||
2858 | 870 | |||
2859 | 871 | def test_credentials_not_found(self): | ||
2860 | 872 | """The CredentialsNotFound signal.""" | ||
2861 | 873 | self.obj.CredentialsNotFound(APP_NAME) | ||
2862 | 874 | msgs = (self.obj.__class__.__name__, | ||
2863 | 875 | self.obj.CredentialsNotFound.__name__, APP_NAME) | ||
2864 | 876 | self.assertTrue(self.memento.check_info(*msgs)) | ||
2865 | 877 | |||
2866 | 878 | def test_credentials_cleared(self): | ||
2867 | 879 | """The CredentialsCleared signal.""" | ||
2868 | 880 | self.obj.CredentialsCleared(APP_NAME) | ||
2869 | 881 | msgs = (self.obj.__class__.__name__, | ||
2870 | 882 | self.obj.CredentialsCleared.__name__, APP_NAME) | ||
2871 | 883 | self.assertTrue(self.memento.check_info(*msgs)) | ||
2872 | 884 | |||
2873 | 885 | def test_credentials_stored(self): | ||
2874 | 886 | """The CredentialsStored signal.""" | ||
2875 | 887 | self.obj.CredentialsStored(APP_NAME) | ||
2876 | 888 | msgs = (self.obj.__class__.__name__, | ||
2877 | 889 | self.obj.CredentialsStored.__name__, APP_NAME) | ||
2878 | 890 | self.assertTrue(self.memento.check_info(*msgs)) | ||
2879 | 891 | |||
2880 | 892 | def test_credentials_error(self): | ||
2881 | 893 | """The CredentialsError signal.""" | ||
2882 | 894 | error = {'error_message': 'failed!', 'detailed error': 'yadda yadda'} | ||
2883 | 895 | self.obj.CredentialsError(APP_NAME, error) | ||
2884 | 896 | msgs = (self.obj.__class__.__name__, | ||
2885 | 897 | self.obj.CredentialsError.__name__, | ||
2886 | 898 | APP_NAME, str(error)) | ||
2887 | 899 | self.assertTrue(self.memento.check_error(*msgs)) | ||
2888 | 900 | |||
2889 | 901 | def test_authorization_denied(self): | ||
2890 | 902 | """The AuthorizationDenied signal.""" | ||
2891 | 903 | self.obj.AuthorizationDenied(APP_NAME) | ||
2892 | 904 | msgs = (self.obj.__class__.__name__, | ||
2893 | 905 | self.obj.AuthorizationDenied.__name__, APP_NAME) | ||
2894 | 906 | self.assertTrue(self.memento.check_info(*msgs)) | ||
2895 | 907 | |||
2896 | 908 | |||
2897 | 909 | class UbuntuSSOServiceTestCase(BaseTestCase): | ||
2898 | 910 | """Test suite for the UbuntuSSOService class.""" | ||
2899 | 911 | |||
2900 | 912 | @defer.inlineCallbacks | ||
2901 | 913 | def setUp(self): | ||
2902 | 914 | yield super(UbuntuSSOServiceTestCase, self).setUp() | ||
2903 | 915 | self.proxy.sso_login = object() | ||
2904 | 916 | self.proxy.cred_manager = object() | ||
2905 | 917 | self.params = [] | ||
2906 | 918 | self.patch(main, 'UbuntuSSOProxy', | ||
2907 | 919 | lambda _: self.params.append(_) or self.proxy) | ||
2908 | 920 | self.obj = UbuntuSSOService() | ||
2909 | 921 | yield self.obj.start() | ||
2910 | 922 | |||
2911 | 923 | def test_creation(self): | ||
2912 | 924 | """Creation paremeter is properly used.""" | ||
2913 | 925 | self.assertEqual(self.params, [self.obj]) | ||
2914 | 926 | |||
2915 | 927 | def test_sso_login(self): | ||
2916 | 928 | """Attributes has the expected value.""" | ||
2917 | 929 | self.assertIsInstance(self.obj.sso_login, SSOLogin) | ||
2918 | 930 | self.assertTrue(self.obj.sso_login.proxy is self.proxy.sso_login) | ||
2919 | 931 | |||
2920 | 932 | def test_cred_manager(self): | ||
2921 | 933 | """Attributes has the expected value.""" | ||
2922 | 934 | self.assertIsInstance(self.obj.cred_manager, CredentialsManagement) | ||
2923 | 935 | self.assertTrue(self.obj.cred_manager.proxy is self.proxy.cred_manager) | ||
2924 | 180 | 936 | ||
2925 | === removed file 'ubuntu_sso/main/tests/test_linux.py' | |||
2926 | --- ubuntu_sso/main/tests/test_linux.py 2011-12-20 17:33:13 +0000 | |||
2927 | +++ ubuntu_sso/main/tests/test_linux.py 1970-01-01 00:00:00 +0000 | |||
2928 | @@ -1,1275 +0,0 @@ | |||
2929 | 1 | # -*- coding: utf-8 -*- | ||
2930 | 2 | # | ||
2931 | 3 | # test_main - tests for ubuntu_sso.main | ||
2932 | 4 | # | ||
2933 | 5 | # Author: Natalia Bidart <natalia.bidart@canonical.com> | ||
2934 | 6 | # Author: Alejandro J. Cura <alecu@canonical.com> | ||
2935 | 7 | # | ||
2936 | 8 | # Copyright 2009-2010 Canonical Ltd. | ||
2937 | 9 | # | ||
2938 | 10 | # This program is free software: you can redistribute it and/or modify it | ||
2939 | 11 | # under the terms of the GNU General Public License version 3, as published | ||
2940 | 12 | # by the Free Software Foundation. | ||
2941 | 13 | # | ||
2942 | 14 | # This program is distributed in the hope that it will be useful, but | ||
2943 | 15 | # WITHOUT ANY WARRANTY; without even the implied warranties of | ||
2944 | 16 | # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
2945 | 17 | # PURPOSE. See the GNU General Public License for more details. | ||
2946 | 18 | # | ||
2947 | 19 | # You should have received a copy of the GNU General Public License along | ||
2948 | 20 | # with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2949 | 21 | """Tests for the main SSO client code.""" | ||
2950 | 22 | |||
2951 | 23 | import logging | ||
2952 | 24 | |||
2953 | 25 | from mocker import Mocker, ARGS, KWARGS | ||
2954 | 26 | from twisted.internet import defer | ||
2955 | 27 | from ubuntuone.devtools.handlers import MementoHandler | ||
2956 | 28 | |||
2957 | 29 | import ubuntu_sso.keyring | ||
2958 | 30 | import ubuntu_sso.main | ||
2959 | 31 | import ubuntu_sso.main.linux | ||
2960 | 32 | |||
2961 | 33 | from ubuntu_sso import DBUS_CREDENTIALS_IFACE | ||
2962 | 34 | from ubuntu_sso.main import ( | ||
2963 | 35 | except_to_errdict, | ||
2964 | 36 | CredentialsManagement, | ||
2965 | 37 | SSOLogin, | ||
2966 | 38 | TIMEOUT_INTERVAL, | ||
2967 | 39 | ) | ||
2968 | 40 | from ubuntu_sso.main.linux import blocking | ||
2969 | 41 | from ubuntu_sso.main import (HELP_TEXT_KEY, PING_URL_KEY, | ||
2970 | 42 | TC_URL_KEY, UI_CLASS_KEY, UI_MODULE_KEY, WINDOW_ID_KEY, | ||
2971 | 43 | SUCCESS_CB_KEY, ERROR_CB_KEY, DENIAL_CB_KEY) | ||
2972 | 44 | from ubuntu_sso.tests import (APP_NAME, TC_URL, HELP_TEXT, CAPTCHA_ID, | ||
2973 | 45 | CAPTCHA_SOLUTION, EMAIL, EMAIL_TOKEN, NAME, PASSWORD, PING_URL, TOKEN, | ||
2974 | 46 | TOKEN_NAME, WINDOW_ID, TestCase) | ||
2975 | 47 | |||
2976 | 48 | |||
2977 | 49 | # Access to a protected member 'yyy' of a client class | ||
2978 | 50 | # pylint: disable=W0212 | ||
2979 | 51 | |||
2980 | 52 | |||
2981 | 53 | class BlockingSampleException(Exception): | ||
2982 | 54 | """The exception that will be thrown by the fake blocking.""" | ||
2983 | 55 | |||
2984 | 56 | |||
2985 | 57 | def fake_ok_blocking(f, app, cb, eb): | ||
2986 | 58 | """A fake blocking function that succeeds.""" | ||
2987 | 59 | cb(app, f()) | ||
2988 | 60 | |||
2989 | 61 | |||
2990 | 62 | def fake_err_blocking(f, app, cb, eb): | ||
2991 | 63 | """A fake blocking function that fails.""" | ||
2992 | 64 | try: | ||
2993 | 65 | f() | ||
2994 | 66 | except Exception, e: # pylint: disable=W0703 | ||
2995 | 67 | eb(app, except_to_errdict(e)) | ||
2996 | 68 | else: | ||
2997 | 69 | eb(app, except_to_errdict(BlockingSampleException())) | ||
2998 | 70 | |||
2999 | 71 | |||
3000 | 72 | class SsoDbusTestCase(TestCase): | ||
3001 | 73 | """Test the SSOLogin DBus interface.""" | ||
3002 | 74 | |||
3003 | 75 | timeout = 2 | ||
3004 | 76 | |||
3005 | 77 | @defer.inlineCallbacks | ||
3006 | 78 | def setUp(self): | ||
3007 | 79 | """Create the mocking bus.""" | ||
3008 | 80 | yield super(SsoDbusTestCase, self).setUp() | ||
3009 | 81 | self.mocker = Mocker() | ||
3010 | 82 | self.mockbusname = self.mocker.mock() | ||
3011 | 83 | mockbus = self.mocker.mock() | ||
3012 | 84 | self.mockbusname.get_bus() | ||
3013 | 85 | self.mocker.result(mockbus) | ||
3014 | 86 | mockbus._register_object_path(ARGS) | ||
3015 | 87 | self.mockprocessorclass = None | ||
3016 | 88 | |||
3017 | 89 | def ksc(keyring, k, val): | ||
3018 | 90 | """Assert over token and app_name.""" | ||
3019 | 91 | self.assertEqual(k, APP_NAME) | ||
3020 | 92 | self.assertEqual(val, TOKEN) | ||
3021 | 93 | self.keyring_was_set = True | ||
3022 | 94 | self.keyring_values = k, val | ||
3023 | 95 | return defer.succeed(None) | ||
3024 | 96 | |||
3025 | 97 | self.patch(ubuntu_sso.main.Keyring, "set_credentials", ksc) | ||
3026 | 98 | self.keyring_was_set = False | ||
3027 | 99 | self.keyring_values = None | ||
3028 | 100 | |||
3029 | 101 | @defer.inlineCallbacks | ||
3030 | 102 | def tearDown(self): | ||
3031 | 103 | """Verify the mocking bus and shut it down.""" | ||
3032 | 104 | self.mocker.verify() | ||
3033 | 105 | self.mocker.restore() | ||
3034 | 106 | yield super(SsoDbusTestCase, self).tearDown() | ||
3035 | 107 | |||
3036 | 108 | def test_creation(self): | ||
3037 | 109 | """Test that the object creation is successful.""" | ||
3038 | 110 | self.mocker.replay() | ||
3039 | 111 | SSOLogin(self.mockbusname) | ||
3040 | 112 | |||
3041 | 113 | def create_mock_processor(self): | ||
3042 | 114 | """Create a mock processor from a dummy processor class.""" | ||
3043 | 115 | self.mockprocessorclass = self.mocker.mock() | ||
3044 | 116 | mockprocessor = self.mocker.mock() | ||
3045 | 117 | self.mockprocessorclass(ARGS, KWARGS) | ||
3046 | 118 | self.mocker.result(mockprocessor) | ||
3047 | 119 | return mockprocessor | ||
3048 | 120 | |||
3049 | 121 | def test_generate_captcha(self): | ||
3050 | 122 | """Test that the captcha method works ok.""" | ||
3051 | 123 | d = defer.Deferred() | ||
3052 | 124 | filename = "sample filename" | ||
3053 | 125 | expected_result = "expected result" | ||
3054 | 126 | self.create_mock_processor().generate_captcha(filename) | ||
3055 | 127 | self.mocker.result(expected_result) | ||
3056 | 128 | self.patch(ubuntu_sso.main.linux, "blocking", fake_ok_blocking) | ||
3057 | 129 | self.mocker.replay() | ||
3058 | 130 | |||
3059 | 131 | def verify(app_name, result): | ||
3060 | 132 | """The actual test.""" | ||
3061 | 133 | self.assertEqual(result, expected_result) | ||
3062 | 134 | self.assertEqual(app_name, APP_NAME) | ||
3063 | 135 | d.callback(result) | ||
3064 | 136 | |||
3065 | 137 | client = SSOLogin(self.mockbusname, | ||
3066 | 138 | sso_login_processor_class=self.mockprocessorclass) | ||
3067 | 139 | self.patch(client, "CaptchaGenerated", verify) | ||
3068 | 140 | self.patch(client, "CaptchaGenerationError", d.errback) | ||
3069 | 141 | client.generate_captcha(APP_NAME, filename) | ||
3070 | 142 | return d | ||
3071 | 143 | |||
3072 | 144 | def test_generate_captcha_error(self): | ||
3073 | 145 | """Test that the captcha method fails as expected.""" | ||
3074 | 146 | d = defer.Deferred() | ||
3075 | 147 | filename = "sample filename" | ||
3076 | 148 | expected_result = "expected result" | ||
3077 | 149 | self.create_mock_processor().generate_captcha(filename) | ||
3078 | 150 | self.mocker.result(expected_result) | ||
3079 | 151 | self.patch(ubuntu_sso.main, "thread_execute", fake_err_blocking) | ||
3080 | 152 | self.mocker.replay() | ||
3081 | 153 | |||
3082 | 154 | def verify(app_name, errdict): | ||
3083 | 155 | """The actual test.""" | ||
3084 | 156 | self.assertEqual(errdict["errtype"], "BlockingSampleException") | ||
3085 | 157 | self.assertEqual(app_name, APP_NAME) | ||
3086 | 158 | d.callback("Ok") | ||
3087 | 159 | |||
3088 | 160 | client = SSOLogin(self.mockbusname, | ||
3089 | 161 | sso_login_processor_class=self.mockprocessorclass) | ||
3090 | 162 | self.patch(client, "CaptchaGenerated", d.errback) | ||
3091 | 163 | self.patch(client, "CaptchaGenerationError", verify) | ||
3092 | 164 | client.generate_captcha(APP_NAME, filename) | ||
3093 | 165 | return d | ||
3094 | 166 | |||
3095 | 167 | def test_register_user(self): | ||
3096 | 168 | """Test that the register_user method works ok.""" | ||
3097 | 169 | d = defer.Deferred() | ||
3098 | 170 | expected_result = "expected result" | ||
3099 | 171 | self.create_mock_processor().register_user(EMAIL, PASSWORD, NAME, | ||
3100 | 172 | CAPTCHA_ID, CAPTCHA_SOLUTION) | ||
3101 | 173 | self.mocker.result(expected_result) | ||
3102 | 174 | self.patch(ubuntu_sso.main.linux, "blocking", fake_ok_blocking) | ||
3103 | 175 | self.mocker.replay() | ||
3104 | 176 | |||
3105 | 177 | def verify(app_name, result): | ||
3106 | 178 | """The actual test.""" | ||
3107 | 179 | self.assertEqual(result, expected_result) | ||
3108 | 180 | self.assertEqual(app_name, APP_NAME) | ||
3109 | 181 | d.callback(result) | ||
3110 | 182 | |||
3111 | 183 | client = SSOLogin(self.mockbusname, | ||
3112 | 184 | sso_login_processor_class=self.mockprocessorclass) | ||
3113 | 185 | self.patch(client, "UserRegistered", verify) | ||
3114 | 186 | self.patch(client, "UserRegistrationError", d.errback) | ||
3115 | 187 | client.register_user(APP_NAME, EMAIL, PASSWORD, NAME, CAPTCHA_ID, | ||
3116 | 188 | CAPTCHA_SOLUTION) | ||
3117 | 189 | return d | ||
3118 | 190 | |||
3119 | 191 | def test_register_user_error(self): | ||
3120 | 192 | """Test that the register_user method fails as expected.""" | ||
3121 | 193 | d = defer.Deferred() | ||
3122 | 194 | expected_result = "expected result" | ||
3123 | 195 | self.create_mock_processor().register_user(EMAIL, PASSWORD, NAME, | ||
3124 | 196 | CAPTCHA_ID, CAPTCHA_SOLUTION) | ||
3125 | 197 | self.mocker.result(expected_result) | ||
3126 | 198 | self.patch(ubuntu_sso.main, "thread_execute", fake_err_blocking) | ||
3127 | 199 | self.mocker.replay() | ||
3128 | 200 | |||
3129 | 201 | def verify(app_name, errdict): | ||
3130 | 202 | """The actual test.""" | ||
3131 | 203 | self.assertEqual(errdict["errtype"], "BlockingSampleException") | ||
3132 | 204 | self.assertEqual(app_name, APP_NAME) | ||
3133 | 205 | d.callback("Ok") | ||
3134 | 206 | |||
3135 | 207 | client = SSOLogin(self.mockbusname, | ||
3136 | 208 | sso_login_processor_class=self.mockprocessorclass) | ||
3137 | 209 | self.patch(client, "UserRegistered", d.errback) | ||
3138 | 210 | self.patch(client, "UserRegistrationError", verify) | ||
3139 | 211 | client.register_user(APP_NAME, EMAIL, PASSWORD, NAME, CAPTCHA_ID, | ||
3140 | 212 | CAPTCHA_SOLUTION) | ||
3141 | 213 | return d | ||
3142 | 214 | |||
3143 | 215 | def test_login(self): | ||
3144 | 216 | """Test that the login method works ok.""" | ||
3145 | 217 | d = defer.Deferred() | ||
3146 | 218 | processor = self.create_mock_processor() | ||
3147 | 219 | processor.login(EMAIL, PASSWORD, TOKEN_NAME) | ||
3148 | 220 | self.mocker.result(TOKEN) | ||
3149 | 221 | processor.is_validated(TOKEN) | ||
3150 | 222 | self.mocker.result(True) | ||
3151 | 223 | self.patch(ubuntu_sso.main.linux, "blocking", fake_ok_blocking) | ||
3152 | 224 | self.mocker.replay() | ||
3153 | 225 | |||
3154 | 226 | def verify(app_name, result): | ||
3155 | 227 | """The actual test.""" | ||
3156 | 228 | self.assertEqual(result, EMAIL) | ||
3157 | 229 | self.assertEqual(app_name, APP_NAME) | ||
3158 | 230 | self.assertTrue(self.keyring_was_set, "The keyring should be set") | ||
3159 | 231 | d.callback(result) | ||
3160 | 232 | |||
3161 | 233 | client = SSOLogin(self.mockbusname, | ||
3162 | 234 | sso_login_processor_class=self.mockprocessorclass) | ||
3163 | 235 | self.patch(client, "LoggedIn", verify) | ||
3164 | 236 | self.patch(client, "LoginError", d.errback) | ||
3165 | 237 | self.patch(client, "UserNotValidated", d.errback) | ||
3166 | 238 | client.login(APP_NAME, EMAIL, PASSWORD) | ||
3167 | 239 | return d | ||
3168 | 240 | |||
3169 | 241 | def test_login_user_not_validated(self): | ||
3170 | 242 | """Test that the login sends EmailNotValidated signal.""" | ||
3171 | 243 | d = defer.Deferred() | ||
3172 | 244 | processor = self.create_mock_processor() | ||
3173 | 245 | processor.login(EMAIL, PASSWORD, TOKEN_NAME) | ||
3174 | 246 | self.mocker.result(TOKEN) | ||
3175 | 247 | processor.is_validated(TOKEN) | ||
3176 | 248 | self.mocker.result(False) | ||
3177 | 249 | self.patch(ubuntu_sso.main.linux, "blocking", fake_ok_blocking) | ||
3178 | 250 | self.mocker.replay() | ||
3179 | 251 | |||
3180 | 252 | def verify(app_name, email): | ||
3181 | 253 | """The actual test.""" | ||
3182 | 254 | self.assertEqual(app_name, APP_NAME) | ||
3183 | 255 | self.assertEqual(email, EMAIL) | ||
3184 | 256 | self.assertFalse(self.keyring_was_set, "Keyring should not be set") | ||
3185 | 257 | d.callback("Ok") | ||
3186 | 258 | |||
3187 | 259 | client = SSOLogin(self.mockbusname, | ||
3188 | 260 | sso_login_processor_class=self.mockprocessorclass) | ||
3189 | 261 | self.patch(client, "LoggedIn", d.errback) | ||
3190 | 262 | self.patch(client, "LoginError", d.errback) | ||
3191 | 263 | self.patch(client, "UserNotValidated", verify) | ||
3192 | 264 | client.login(APP_NAME, EMAIL, PASSWORD) | ||
3193 | 265 | return d | ||
3194 | 266 | |||
3195 | 267 | def test_login_error_get_token_name(self): | ||
3196 | 268 | """The login method fails as expected when get_token_name fails.""" | ||
3197 | 269 | d = defer.Deferred() | ||
3198 | 270 | self.create_mock_processor() | ||
3199 | 271 | self.patch(ubuntu_sso.main.linux, "blocking", fake_err_blocking) | ||
3200 | 272 | |||
3201 | 273 | def fake_gtn(*args): | ||
3202 | 274 | """A fake get_token_name that fails.""" | ||
3203 | 275 | raise BlockingSampleException() | ||
3204 | 276 | |||
3205 | 277 | self.patch(ubuntu_sso.main, "get_token_name", fake_gtn) | ||
3206 | 278 | self.mocker.replay() | ||
3207 | 279 | |||
3208 | 280 | def verify(app_name, errdict): | ||
3209 | 281 | """The actual test.""" | ||
3210 | 282 | self.assertEqual(app_name, APP_NAME) | ||
3211 | 283 | self.assertEqual(errdict["errtype"], "BlockingSampleException") | ||
3212 | 284 | self.assertFalse(self.keyring_was_set, "Keyring should not be set") | ||
3213 | 285 | d.callback("Ok") | ||
3214 | 286 | |||
3215 | 287 | client = SSOLogin(self.mockbusname, | ||
3216 | 288 | sso_login_processor_class=self.mockprocessorclass) | ||
3217 | 289 | self.patch(client, "LoggedIn", d.errback) | ||
3218 | 290 | self.patch(client, "LoginError", verify) | ||
3219 | 291 | self.patch(client, "UserNotValidated", d.errback) | ||
3220 | 292 | client.login(APP_NAME, EMAIL, PASSWORD) | ||
3221 | 293 | return d | ||
3222 | 294 | |||
3223 | 295 | def test_login_error_set_credentials(self): | ||
3224 | 296 | """The login method fails as expected when set_credentials fails.""" | ||
3225 | 297 | d = defer.Deferred() | ||
3226 | 298 | processor = self.create_mock_processor() | ||
3227 | 299 | processor.login(EMAIL, PASSWORD, TOKEN_NAME) | ||
3228 | 300 | self.mocker.result(TOKEN) | ||
3229 | 301 | processor.is_validated(TOKEN) | ||
3230 | 302 | self.mocker.result(True) | ||
3231 | 303 | |||
3232 | 304 | self.patch(ubuntu_sso.main.linux, "blocking", fake_ok_blocking) | ||
3233 | 305 | |||
3234 | 306 | def fake_set_creds(*args): | ||
3235 | 307 | """A fake Keyring.set_credentials that fails.""" | ||
3236 | 308 | return defer.fail(BlockingSampleException()) | ||
3237 | 309 | |||
3238 | 310 | self.patch(ubuntu_sso.main.Keyring, "set_credentials", fake_set_creds) | ||
3239 | 311 | self.mocker.replay() | ||
3240 | 312 | |||
3241 | 313 | def verify(app_name, errdict): | ||
3242 | 314 | """The actual test.""" | ||
3243 | 315 | self.assertEqual(app_name, APP_NAME) | ||
3244 | 316 | self.assertEqual(errdict["errtype"], "BlockingSampleException") | ||
3245 | 317 | self.assertFalse(self.keyring_was_set, "Keyring should not be set") | ||
3246 | 318 | d.callback("Ok") | ||
3247 | 319 | |||
3248 | 320 | client = SSOLogin(self.mockbusname, | ||
3249 | 321 | sso_login_processor_class=self.mockprocessorclass) | ||
3250 | 322 | fail = lambda app, res: d.errback((app, res)) | ||
3251 | 323 | self.patch(client, "LoggedIn", fail) | ||
3252 | 324 | self.patch(client, "LoginError", verify) | ||
3253 | 325 | self.patch(client, "UserNotValidated", fail) | ||
3254 | 326 | client.login(APP_NAME, EMAIL, PASSWORD) | ||
3255 | 327 | return d | ||
3256 | 328 | |||
3257 | 329 | def test_validate_email(self): | ||
3258 | 330 | """Test that the validate_email method works ok.""" | ||
3259 | 331 | d = defer.Deferred() | ||
3260 | 332 | self.create_mock_processor().validate_email(EMAIL, PASSWORD, | ||
3261 | 333 | EMAIL_TOKEN, TOKEN_NAME) | ||
3262 | 334 | self.mocker.result(TOKEN) | ||
3263 | 335 | self.patch(ubuntu_sso.main.linux, "blocking", fake_ok_blocking) | ||
3264 | 336 | self.mocker.replay() | ||
3265 | 337 | |||
3266 | 338 | def verify(app_name, result): | ||
3267 | 339 | """The actual test.""" | ||
3268 | 340 | self.assertEqual(result, EMAIL) | ||
3269 | 341 | self.assertEqual(app_name, APP_NAME) | ||
3270 | 342 | self.assertTrue(self.keyring_was_set, "The keyring should be set") | ||
3271 | 343 | d.callback(result) | ||
3272 | 344 | |||
3273 | 345 | client = SSOLogin(self.mockbusname, | ||
3274 | 346 | sso_login_processor_class=self.mockprocessorclass) | ||
3275 | 347 | self.patch(client, "EmailValidated", verify) | ||
3276 | 348 | self.patch(client, "EmailValidationError", d.errback) | ||
3277 | 349 | client.validate_email(APP_NAME, EMAIL, PASSWORD, EMAIL_TOKEN) | ||
3278 | 350 | return d | ||
3279 | 351 | |||
3280 | 352 | def test_validate_email_error(self): | ||
3281 | 353 | """Test that the validate_email method fails as expected.""" | ||
3282 | 354 | d = defer.Deferred() | ||
3283 | 355 | self.create_mock_processor() | ||
3284 | 356 | self.patch(ubuntu_sso.main.linux, "blocking", fake_err_blocking) | ||
3285 | 357 | |||
3286 | 358 | def fake_gtn(*args): | ||
3287 | 359 | """A fake get_token_name that fails.""" | ||
3288 | 360 | raise BlockingSampleException() | ||
3289 | 361 | |||
3290 | 362 | self.patch(ubuntu_sso.main, "get_token_name", fake_gtn) | ||
3291 | 363 | self.mocker.replay() | ||
3292 | 364 | |||
3293 | 365 | def verify(app_name, errdict): | ||
3294 | 366 | """The actual test.""" | ||
3295 | 367 | self.assertEqual(app_name, APP_NAME) | ||
3296 | 368 | self.assertEqual(errdict["errtype"], "BlockingSampleException") | ||
3297 | 369 | self.assertFalse(self.keyring_was_set, "Keyring should not be set") | ||
3298 | 370 | d.callback("Ok") | ||
3299 | 371 | |||
3300 | 372 | client = SSOLogin(self.mockbusname, | ||
3301 | 373 | sso_login_processor_class=self.mockprocessorclass) | ||
3302 | 374 | self.patch(client, "EmailValidated", d.errback) | ||
3303 | 375 | self.patch(client, "EmailValidationError", verify) | ||
3304 | 376 | client.validate_email(APP_NAME, EMAIL, PASSWORD, EMAIL_TOKEN) | ||
3305 | 377 | return d | ||
3306 | 378 | |||
3307 | 379 | def test_request_password_reset_token(self): | ||
3308 | 380 | """Test that the request_password_reset_token method works ok.""" | ||
3309 | 381 | d = defer.Deferred() | ||
3310 | 382 | processor = self.create_mock_processor() | ||
3311 | 383 | processor.request_password_reset_token(EMAIL) | ||
3312 | 384 | self.patch(ubuntu_sso.main, "thread_execute", fake_ok_blocking) | ||
3313 | 385 | self.mocker.result(EMAIL) | ||
3314 | 386 | self.mocker.replay() | ||
3315 | 387 | |||
3316 | 388 | def verify(app_name, result): | ||
3317 | 389 | """The actual test.""" | ||
3318 | 390 | self.assertEqual(result, EMAIL) | ||
3319 | 391 | self.assertEqual(app_name, APP_NAME) | ||
3320 | 392 | d.callback(result) | ||
3321 | 393 | |||
3322 | 394 | client = SSOLogin(self.mockbusname, | ||
3323 | 395 | sso_login_processor_class=self.mockprocessorclass) | ||
3324 | 396 | self.patch(client, "PasswordResetTokenSent", verify) | ||
3325 | 397 | self.patch(client, "PasswordResetError", d.errback) | ||
3326 | 398 | client.request_password_reset_token(APP_NAME, EMAIL) | ||
3327 | 399 | return d | ||
3328 | 400 | |||
3329 | 401 | def test_request_password_reset_token_error(self): | ||
3330 | 402 | """Test the request_password_reset_token method fails as expected.""" | ||
3331 | 403 | d = defer.Deferred() | ||
3332 | 404 | |||
3333 | 405 | self.create_mock_processor().request_password_reset_token(EMAIL) | ||
3334 | 406 | self.mocker.result(EMAIL) | ||
3335 | 407 | self.patch(ubuntu_sso.main, "thread_execute", fake_err_blocking) | ||
3336 | 408 | self.mocker.replay() | ||
3337 | 409 | |||
3338 | 410 | def verify(app_name, errdict): | ||
3339 | 411 | """The actual test.""" | ||
3340 | 412 | self.assertEqual(errdict["errtype"], "BlockingSampleException") | ||
3341 | 413 | self.assertEqual(app_name, APP_NAME) | ||
3342 | 414 | d.callback("Ok") | ||
3343 | 415 | |||
3344 | 416 | client = SSOLogin(self.mockbusname, | ||
3345 | 417 | sso_login_processor_class=self.mockprocessorclass) | ||
3346 | 418 | self.patch(client, "PasswordResetTokenSent", d.errback) | ||
3347 | 419 | self.patch(client, "PasswordResetError", verify) | ||
3348 | 420 | client.request_password_reset_token(APP_NAME, EMAIL) | ||
3349 | 421 | return d | ||
3350 | 422 | |||
3351 | 423 | def test_set_new_password(self): | ||
3352 | 424 | """Test that the set_new_password method works ok.""" | ||
3353 | 425 | d = defer.Deferred() | ||
3354 | 426 | self.create_mock_processor().set_new_password(EMAIL, EMAIL_TOKEN, | ||
3355 | 427 | PASSWORD) | ||
3356 | 428 | self.mocker.result(EMAIL) | ||
3357 | 429 | self.patch(ubuntu_sso.main.linux, "blocking", fake_ok_blocking) | ||
3358 | 430 | self.mocker.replay() | ||
3359 | 431 | |||
3360 | 432 | def verify(app_name, result): | ||
3361 | 433 | """The actual test.""" | ||
3362 | 434 | self.assertEqual(result, EMAIL) | ||
3363 | 435 | self.assertEqual(app_name, APP_NAME) | ||
3364 | 436 | d.callback(result) | ||
3365 | 437 | |||
3366 | 438 | client = SSOLogin(self.mockbusname, | ||
3367 | 439 | sso_login_processor_class=self.mockprocessorclass) | ||
3368 | 440 | self.patch(client, "PasswordChanged", verify) | ||
3369 | 441 | self.patch(client, "PasswordChangeError", d.errback) | ||
3370 | 442 | client.set_new_password(APP_NAME, EMAIL, EMAIL_TOKEN, PASSWORD) | ||
3371 | 443 | return d | ||
3372 | 444 | |||
3373 | 445 | def test_set_new_password_error(self): | ||
3374 | 446 | """Test that the set_new_password method fails as expected.""" | ||
3375 | 447 | d = defer.Deferred() | ||
3376 | 448 | expected_result = "expected result" | ||
3377 | 449 | |||
3378 | 450 | self.create_mock_processor().set_new_password(EMAIL, EMAIL_TOKEN, | ||
3379 | 451 | PASSWORD) | ||
3380 | 452 | self.mocker.result(expected_result) | ||
3381 | 453 | self.patch(ubuntu_sso.main, "thread_execute", fake_err_blocking) | ||
3382 | 454 | self.mocker.replay() | ||
3383 | 455 | |||
3384 | 456 | def verify(app_name, errdict): | ||
3385 | 457 | """The actual test.""" | ||
3386 | 458 | self.assertEqual(errdict["errtype"], "BlockingSampleException") | ||
3387 | 459 | self.assertEqual(app_name, APP_NAME) | ||
3388 | 460 | d.callback("Ok") | ||
3389 | 461 | |||
3390 | 462 | client = SSOLogin(self.mockbusname, | ||
3391 | 463 | sso_login_processor_class=self.mockprocessorclass) | ||
3392 | 464 | self.patch(client, "PasswordChanged", d.errback) | ||
3393 | 465 | self.patch(client, "PasswordChangeError", verify) | ||
3394 | 466 | client.set_new_password(APP_NAME, EMAIL, EMAIL_TOKEN, PASSWORD) | ||
3395 | 467 | return d | ||
3396 | 468 | |||
3397 | 469 | |||
3398 | 470 | class BlockingFunctionTestCase(TestCase): | ||
3399 | 471 | """Tests for the "blocking" function.""" | ||
3400 | 472 | |||
3401 | 473 | timeout = 5 | ||
3402 | 474 | |||
3403 | 475 | def test_blocking(self): | ||
3404 | 476 | """Test the normal behaviour.""" | ||
3405 | 477 | d = defer.Deferred() | ||
3406 | 478 | expected_result = "expected result" | ||
3407 | 479 | |||
3408 | 480 | def f(): | ||
3409 | 481 | """No failure.""" | ||
3410 | 482 | return expected_result | ||
3411 | 483 | |||
3412 | 484 | def verify(app_name, result): | ||
3413 | 485 | """The actual test.""" | ||
3414 | 486 | self.assertEqual(result, expected_result) | ||
3415 | 487 | self.assertEqual(app_name, APP_NAME) | ||
3416 | 488 | d.callback(result) | ||
3417 | 489 | |||
3418 | 490 | blocking(f, APP_NAME, verify, d.errback) | ||
3419 | 491 | return d | ||
3420 | 492 | |||
3421 | 493 | def test_blocking_error(self): | ||
3422 | 494 | """Test the behaviour when an Exception is raised.""" | ||
3423 | 495 | d = defer.Deferred() | ||
3424 | 496 | expected_error_message = "expected error message" | ||
3425 | 497 | |||
3426 | 498 | def f(): | ||
3427 | 499 | """Failure.""" | ||
3428 | 500 | raise BlockingSampleException(expected_error_message) | ||
3429 | 501 | |||
3430 | 502 | def verify(app_name, errdict): | ||
3431 | 503 | """The actual test.""" | ||
3432 | 504 | self.assertEqual(app_name, APP_NAME) | ||
3433 | 505 | self.assertEqual(errdict["errtype"], "BlockingSampleException") | ||
3434 | 506 | self.assertEqual(errdict["message"], expected_error_message) | ||
3435 | 507 | d.callback("Ok") | ||
3436 | 508 | |||
3437 | 509 | blocking(f, APP_NAME, d.errback, verify) | ||
3438 | 510 | return d | ||
3439 | 511 | |||
3440 | 512 | |||
3441 | 513 | class TestExceptToErrdictException(Exception): | ||
3442 | 514 | """A dummy exception for the following testcase.""" | ||
3443 | 515 | |||
3444 | 516 | |||
3445 | 517 | class ExceptToErrdictTestCase(TestCase): | ||
3446 | 518 | """Tests for the except_to_errdict function.""" | ||
3447 | 519 | |||
3448 | 520 | def test_first_arg_is_dict(self): | ||
3449 | 521 | """If the first arg is a dict, use it as the base dict.""" | ||
3450 | 522 | sample_dict = { | ||
3451 | 523 | "errorcode1": "error message 1", | ||
3452 | 524 | "errorcode2": "error message 2", | ||
3453 | 525 | "errorcode3": "error message 3", | ||
3454 | 526 | } | ||
3455 | 527 | e = TestExceptToErrdictException(sample_dict) | ||
3456 | 528 | result = except_to_errdict(e) | ||
3457 | 529 | |||
3458 | 530 | self.assertEqual(result["errtype"], e.__class__.__name__) | ||
3459 | 531 | for k in sample_dict.keys(): | ||
3460 | 532 | self.assertIn(k, result) | ||
3461 | 533 | self.assertEqual(result[k], sample_dict[k]) | ||
3462 | 534 | |||
3463 | 535 | def test_first_arg_is_str(self): | ||
3464 | 536 | """If the first arg is a str, use it as the message.""" | ||
3465 | 537 | sample_string = "a sample string" | ||
3466 | 538 | e = TestExceptToErrdictException(sample_string) | ||
3467 | 539 | result = except_to_errdict(e) | ||
3468 | 540 | self.assertEqual(result["errtype"], e.__class__.__name__) | ||
3469 | 541 | self.assertEqual(result["message"], sample_string) | ||
3470 | 542 | |||
3471 | 543 | def test_first_arg_is_unicode(self): | ||
3472 | 544 | """If the first arg is a unicode, use it as the message.""" | ||
3473 | 545 | sample_string = u"a sample string" | ||
3474 | 546 | e = TestExceptToErrdictException(sample_string) | ||
3475 | 547 | result = except_to_errdict(e) | ||
3476 | 548 | self.assertEqual(result["errtype"], e.__class__.__name__) | ||
3477 | 549 | self.assertEqual(result["message"], sample_string) | ||
3478 | 550 | |||
3479 | 551 | def test_no_args_at_all(self): | ||
3480 | 552 | """If there are no args, use the class docstring.""" | ||
3481 | 553 | e = TestExceptToErrdictException() | ||
3482 | 554 | result = except_to_errdict(e) | ||
3483 | 555 | self.assertEqual(result["errtype"], e.__class__.__name__) | ||
3484 | 556 | self.assertEqual(result["message"], e.__class__.__doc__) | ||
3485 | 557 | |||
3486 | 558 | def test_some_other_thing_as_first_arg(self): | ||
3487 | 559 | """If first arg is not basestring nor dict, then repr all args.""" | ||
3488 | 560 | sample_args = (None, u"unicode2\ufffd", "errorcode3") | ||
3489 | 561 | e = TestExceptToErrdictException(*sample_args) | ||
3490 | 562 | result = except_to_errdict(e) | ||
3491 | 563 | self.assertEqual(result["errtype"], e.__class__.__name__) | ||
3492 | 564 | |||
3493 | 565 | |||
3494 | 566 | class CredentialsManagementTestCase(TestCase): | ||
3495 | 567 | """Tests for the CredentialsManagement DBus interface.""" | ||
3496 | 568 | |||
3497 | 569 | timeout = 2 | ||
3498 | 570 | base_args = {HELP_TEXT_KEY: HELP_TEXT, PING_URL_KEY: PING_URL, | ||
3499 | 571 | TC_URL_KEY: TC_URL, WINDOW_ID_KEY: WINDOW_ID, | ||
3500 | 572 | UI_CLASS_KEY: 'SuperUI', UI_MODULE_KEY: 'foo.bar.baz', | ||
3501 | 573 | } | ||
3502 | 574 | |||
3503 | 575 | @defer.inlineCallbacks | ||
3504 | 576 | def setUp(self): | ||
3505 | 577 | yield super(CredentialsManagementTestCase, self).setUp() | ||
3506 | 578 | |||
3507 | 579 | self.mocker = Mocker() | ||
3508 | 580 | self.client = CredentialsManagement(timeout_func=lambda *a: None, | ||
3509 | 581 | shutdown_func=lambda *a: None) | ||
3510 | 582 | self.args = {} | ||
3511 | 583 | self.cred_args = {} | ||
3512 | 584 | |||
3513 | 585 | self.memento = MementoHandler() | ||
3514 | 586 | self.memento.setLevel(logging.DEBUG) | ||
3515 | 587 | ubuntu_sso.main.logger.addHandler(self.memento) | ||
3516 | 588 | self.addCleanup(ubuntu_sso.main.logger.removeHandler, self.memento) | ||
3517 | 589 | |||
3518 | 590 | @defer.inlineCallbacks | ||
3519 | 591 | def tearDown(self): | ||
3520 | 592 | """Verify the mocking stuff and shut it down.""" | ||
3521 | 593 | self.mocker.verify() | ||
3522 | 594 | self.mocker.restore() | ||
3523 | 595 | yield super(CredentialsManagementTestCase, self).tearDown() | ||
3524 | 596 | |||
3525 | 597 | def assert_dbus_method_correct(self, method, out_signature=''): | ||
3526 | 598 | """Check that 'method' is a dbus method with proper signatures.""" | ||
3527 | 599 | self.assertTrue(method._dbus_is_method) | ||
3528 | 600 | self.assertEqual(method._dbus_interface, DBUS_CREDENTIALS_IFACE) | ||
3529 | 601 | self.assertEqual(method._dbus_in_signature, 'sa{ss}') | ||
3530 | 602 | self.assertEqual(method._dbus_out_signature, out_signature) | ||
3531 | 603 | |||
3532 | 604 | def create_mock_backend(self): | ||
3533 | 605 | """Create a mock backend.""" | ||
3534 | 606 | mock_class = self.mocker.replace("ubuntu_sso.credentials.Credentials") | ||
3535 | 607 | mock_class(APP_NAME, **self.cred_args) | ||
3536 | 608 | creds_obj = self.mocker.mock() | ||
3537 | 609 | self.mocker.result(creds_obj) | ||
3538 | 610 | |||
3539 | 611 | return creds_obj | ||
3540 | 612 | |||
3541 | 613 | def test_is_dbus_object(self): | ||
3542 | 614 | """CredentialsManagement is a Dbus object.""" | ||
3543 | 615 | self.assertIsInstance(self.client, | ||
3544 | 616 | ubuntu_sso.main.linux.dbus.service.Object) | ||
3545 | 617 | |||
3546 | 618 | |||
3547 | 619 | class FakeCredentials(object): | ||
3548 | 620 | """A very dummy Credentials object.""" | ||
3549 | 621 | |||
3550 | 622 | def __init__(self, *a, **kw): | ||
3551 | 623 | self.clear_credentials = lambda *a: defer.succeed(None) | ||
3552 | 624 | self.store_credentials = lambda *a: defer.succeed(None) | ||
3553 | 625 | self.login = self.register = lambda *a: None | ||
3554 | 626 | |||
3555 | 627 | def find_credentials(self, *a, **kw): | ||
3556 | 628 | """Retrieve credentials.""" | ||
3557 | 629 | return defer.succeed(TOKEN) | ||
3558 | 630 | |||
3559 | 631 | |||
3560 | 632 | class CredentialsManagementRefCountingTestCase(CredentialsManagementTestCase): | ||
3561 | 633 | """Tests for the CredentialsManagement ref counting.""" | ||
3562 | 634 | |||
3563 | 635 | @defer.inlineCallbacks | ||
3564 | 636 | def setUp(self): | ||
3565 | 637 | yield super(CredentialsManagementRefCountingTestCase, self).setUp() | ||
3566 | 638 | self.patch(ubuntu_sso.main, 'Credentials', FakeCredentials) | ||
3567 | 639 | |||
3568 | 640 | def test_ref_counting(self): | ||
3569 | 641 | """Ref counting is in place.""" | ||
3570 | 642 | self.assertEqual(self.client.root.ref_count, 0) | ||
3571 | 643 | |||
3572 | 644 | def test_find_credentials(self): | ||
3573 | 645 | """Keep proper track of on going requests.""" | ||
3574 | 646 | d = defer.Deferred() | ||
3575 | 647 | |||
3576 | 648 | def verify(*args): | ||
3577 | 649 | """Make the check.""" | ||
3578 | 650 | self.assertEqual(self.client.root.ref_count, 1) | ||
3579 | 651 | d.callback(True) | ||
3580 | 652 | |||
3581 | 653 | self.patch(self.client, 'CredentialsFound', verify) | ||
3582 | 654 | self.client.find_credentials(APP_NAME, self.args) | ||
3583 | 655 | |||
3584 | 656 | return d | ||
3585 | 657 | |||
3586 | 658 | @defer.inlineCallbacks | ||
3587 | 659 | def test_find_credentials_sync(self): | ||
3588 | 660 | """Keep proper track of on going requests.""" | ||
3589 | 661 | d = defer.Deferred() | ||
3590 | 662 | |||
3591 | 663 | def verify(*args): | ||
3592 | 664 | """Make the check.""" | ||
3593 | 665 | self.assertEqual(self.client.root.ref_count, 1) | ||
3594 | 666 | d.callback(True) | ||
3595 | 667 | |||
3596 | 668 | self.client.find_credentials_sync(APP_NAME, self.args, | ||
3597 | 669 | reply_handler=verify, | ||
3598 | 670 | error_handler=d.errback) | ||
3599 | 671 | yield d | ||
3600 | 672 | self.assertEqual(self.client.root.ref_count, 0) | ||
3601 | 673 | |||
3602 | 674 | @defer.inlineCallbacks | ||
3603 | 675 | def test_find_credentials_sync_error(self): | ||
3604 | 676 | """Keep proper track of on going requests.""" | ||
3605 | 677 | d = defer.Deferred() | ||
3606 | 678 | |||
3607 | 679 | def verify(*args): | ||
3608 | 680 | """Make the check.""" | ||
3609 | 681 | self.assertEqual(self.client.root.ref_count, 1) | ||
3610 | 682 | d.callback(True) | ||
3611 | 683 | |||
3612 | 684 | self.patch(ubuntu_sso.main.Credentials, 'find_credentials', | ||
3613 | 685 | lambda *a: defer.fail('foo')) | ||
3614 | 686 | self.client.find_credentials_sync(APP_NAME, self.args, | ||
3615 | 687 | reply_handler=d.errback, | ||
3616 | 688 | error_handler=verify) | ||
3617 | 689 | |||
3618 | 690 | yield d | ||
3619 | 691 | self.assertEqual(self.client.root.ref_count, 0) | ||
3620 | 692 | |||
3621 | 693 | def test_clear_credentials(self): | ||
3622 | 694 | """Keep proper track of on going requests.""" | ||
3623 | 695 | d = defer.Deferred() | ||
3624 | 696 | |||
3625 | 697 | def verify(*args): | ||
3626 | 698 | """Make the check.""" | ||
3627 | 699 | self.assertEqual(self.client.root.ref_count, 1) | ||
3628 | 700 | d.callback(True) | ||
3629 | 701 | |||
3630 | 702 | self.patch(self.client, 'CredentialsCleared', verify) | ||
3631 | 703 | self.client.clear_credentials(APP_NAME, self.args) | ||
3632 | 704 | |||
3633 | 705 | return d | ||
3634 | 706 | |||
3635 | 707 | def test_store_credentials(self): | ||
3636 | 708 | """Keep proper track of on going requests.""" | ||
3637 | 709 | d = defer.Deferred() | ||
3638 | 710 | |||
3639 | 711 | def verify(*args): | ||
3640 | 712 | """Make the check.""" | ||
3641 | 713 | self.assertEqual(self.client.root.ref_count, 1) | ||
3642 | 714 | d.callback(True) | ||
3643 | 715 | |||
3644 | 716 | self.patch(self.client, 'CredentialsStored', verify) | ||
3645 | 717 | self.client.store_credentials(APP_NAME, self.args) | ||
3646 | 718 | |||
3647 | 719 | return d | ||
3648 | 720 | |||
3649 | 721 | def test_register(self): | ||
3650 | 722 | """Keep proper track of on going requests.""" | ||
3651 | 723 | self.client.register(APP_NAME, self.args) | ||
3652 | 724 | |||
3653 | 725 | self.assertEqual(self.client.root.ref_count, 1) | ||
3654 | 726 | |||
3655 | 727 | def test_login(self): | ||
3656 | 728 | """Keep proper track of on going requests.""" | ||
3657 | 729 | self.client.login(APP_NAME, self.args) | ||
3658 | 730 | |||
3659 | 731 | self.assertEqual(self.client.root.ref_count, 1) | ||
3660 | 732 | |||
3661 | 733 | def test_several_requests(self): | ||
3662 | 734 | """Requests can be nested.""" | ||
3663 | 735 | self.client.login(APP_NAME, self.args) | ||
3664 | 736 | self.client.register(APP_NAME, self.args) | ||
3665 | 737 | self.client.login(APP_NAME, self.args) | ||
3666 | 738 | self.client.register(APP_NAME, self.args) | ||
3667 | 739 | self.client.register(APP_NAME, self.args) | ||
3668 | 740 | |||
3669 | 741 | self.assertEqual(self.client.root.ref_count, 5) | ||
3670 | 742 | |||
3671 | 743 | def test_credentials_found(self): | ||
3672 | 744 | """Ref counter is decreased when a signal is sent.""" | ||
3673 | 745 | self.client.root.ref_count = 3 | ||
3674 | 746 | self.client.CredentialsFound(APP_NAME, TOKEN) | ||
3675 | 747 | |||
3676 | 748 | self.assertEqual(self.client.root.ref_count, 2) | ||
3677 | 749 | |||
3678 | 750 | def test_credentials_not_found(self): | ||
3679 | 751 | """Ref counter is decreased when a signal is sent.""" | ||
3680 | 752 | self.client.root.ref_count = 3 | ||
3681 | 753 | self.client.CredentialsNotFound(APP_NAME) | ||
3682 | 754 | |||
3683 | 755 | self.assertEqual(self.client.root.ref_count, 2) | ||
3684 | 756 | |||
3685 | 757 | def test_credentials_cleared(self): | ||
3686 | 758 | """Ref counter is decreased when a signal is sent.""" | ||
3687 | 759 | self.client.root.ref_count = 3 | ||
3688 | 760 | self.client.CredentialsCleared(APP_NAME) | ||
3689 | 761 | |||
3690 | 762 | self.assertEqual(self.client.root.ref_count, 2) | ||
3691 | 763 | |||
3692 | 764 | def test_credentials_stored(self): | ||
3693 | 765 | """Ref counter is decreased when a signal is sent.""" | ||
3694 | 766 | self.client.root.ref_count = 3 | ||
3695 | 767 | self.client.CredentialsStored(APP_NAME) | ||
3696 | 768 | |||
3697 | 769 | self.assertEqual(self.client.root.ref_count, 2) | ||
3698 | 770 | |||
3699 | 771 | def test_credentials_error(self): | ||
3700 | 772 | """Ref counter is decreased when a signal is sent.""" | ||
3701 | 773 | self.client.root.ref_count = 3 | ||
3702 | 774 | self.client.CredentialsError(APP_NAME, {'error_type': 'test'}) | ||
3703 | 775 | |||
3704 | 776 | self.assertEqual(self.client.root.ref_count, 2) | ||
3705 | 777 | |||
3706 | 778 | def test_authorization_denied(self): | ||
3707 | 779 | """Ref counter is decreased when a signal is sent.""" | ||
3708 | 780 | self.client.root.ref_count = 3 | ||
3709 | 781 | self.client.AuthorizationDenied(APP_NAME) | ||
3710 | 782 | |||
3711 | 783 | self.assertEqual(self.client.root.ref_count, 2) | ||
3712 | 784 | |||
3713 | 785 | def test_credentials_found_when_ref_count_is_not_positive(self): | ||
3714 | 786 | """Ref counter is decreased when a signal is sent.""" | ||
3715 | 787 | self.client.root._ref_count = -3 | ||
3716 | 788 | self.client.CredentialsFound(APP_NAME, TOKEN) | ||
3717 | 789 | |||
3718 | 790 | self.assertEqual(self.client.root.ref_count, 0) | ||
3719 | 791 | msg = 'Attempting to decrease ref_count to a negative value (-4).' | ||
3720 | 792 | self.assertTrue(self.memento.check_warning(msg)) | ||
3721 | 793 | |||
3722 | 794 | def test_credentials_not_found_when_ref_count_is_not_positive(self): | ||
3723 | 795 | """Ref counter is decreased when a signal is sent.""" | ||
3724 | 796 | self.client.root._ref_count = -3 | ||
3725 | 797 | self.client.CredentialsNotFound(APP_NAME) | ||
3726 | 798 | |||
3727 | 799 | self.assertEqual(self.client.root.ref_count, 0) | ||
3728 | 800 | msg = 'Attempting to decrease ref_count to a negative value (-4).' | ||
3729 | 801 | self.assertTrue(self.memento.check_warning(msg)) | ||
3730 | 802 | |||
3731 | 803 | def test_credentials_cleared_when_ref_count_is_not_positive(self): | ||
3732 | 804 | """Ref counter is decreased when a signal is sent.""" | ||
3733 | 805 | self.client.root._ref_count = -3 | ||
3734 | 806 | self.client.CredentialsCleared(APP_NAME) | ||
3735 | 807 | |||
3736 | 808 | self.assertEqual(self.client.root.ref_count, 0) | ||
3737 | 809 | msg = 'Attempting to decrease ref_count to a negative value (-4).' | ||
3738 | 810 | self.assertTrue(self.memento.check_warning(msg)) | ||
3739 | 811 | |||
3740 | 812 | def test_credentials_stored_when_ref_count_is_not_positive(self): | ||
3741 | 813 | """Ref counter is decreased when a signal is sent.""" | ||
3742 | 814 | self.client.root._ref_count = -3 | ||
3743 | 815 | self.client.CredentialsStored(APP_NAME) | ||
3744 | 816 | |||
3745 | 817 | self.assertEqual(self.client.root.ref_count, 0) | ||
3746 | 818 | msg = 'Attempting to decrease ref_count to a negative value (-4).' | ||
3747 | 819 | self.assertTrue(self.memento.check_warning(msg)) | ||
3748 | 820 | |||
3749 | 821 | def test_credentials_error_when_ref_count_is_not_positive(self): | ||
3750 | 822 | """Ref counter is decreased when a signal is sent.""" | ||
3751 | 823 | self.client.root._ref_count = -3 | ||
3752 | 824 | self.client.CredentialsError(APP_NAME, {'error_type': 'test'}) | ||
3753 | 825 | |||
3754 | 826 | self.assertEqual(self.client.root.ref_count, 0) | ||
3755 | 827 | msg = 'Attempting to decrease ref_count to a negative value (-4).' | ||
3756 | 828 | self.assertTrue(self.memento.check_warning(msg)) | ||
3757 | 829 | |||
3758 | 830 | def test_autorization_denied_when_ref_count_is_not_positive(self): | ||
3759 | 831 | """Ref counter is decreased when a signal is sent.""" | ||
3760 | 832 | self.client.root._ref_count = -3 | ||
3761 | 833 | self.client.AuthorizationDenied(APP_NAME) | ||
3762 | 834 | |||
3763 | 835 | self.assertEqual(self.client.root.ref_count, 0) | ||
3764 | 836 | msg = 'Attempting to decrease ref_count to a negative value (-4).' | ||
3765 | 837 | self.assertTrue(self.memento.check_warning(msg)) | ||
3766 | 838 | |||
3767 | 839 | def test_on_zero_ref_count_shutdown(self): | ||
3768 | 840 | """When ref count reaches 0, queue shutdown op.""" | ||
3769 | 841 | self.patch(self.client.root, 'timeout_func', self._set_called) | ||
3770 | 842 | self.client.login(APP_NAME, self.args) | ||
3771 | 843 | self.client.CredentialsFound(APP_NAME, TOKEN) | ||
3772 | 844 | |||
3773 | 845 | self.assertEqual(self._called, | ||
3774 | 846 | ((TIMEOUT_INTERVAL, self.client.root.shutdown), {})) | ||
3775 | 847 | |||
3776 | 848 | def test_on_non_zero_ref_count_do_not_shutdown(self): | ||
3777 | 849 | """If ref count is not 0, do not queue shutdown op.""" | ||
3778 | 850 | self.patch(self.client.root, 'timeout_func', self._set_called) | ||
3779 | 851 | self.client.login(APP_NAME, self.args) | ||
3780 | 852 | |||
3781 | 853 | self.assertEqual(self._called, False) | ||
3782 | 854 | |||
3783 | 855 | def test_on_non_zero_ref_count_after_zero_do_not_shutdown(self): | ||
3784 | 856 | """If the shutdown was queued, do not quit if counter is not zero.""" | ||
3785 | 857 | |||
3786 | 858 | def fake_timeout_func(interval, func): | ||
3787 | 859 | """Start a new request when the timer is started.""" | ||
3788 | 860 | self.client.register(APP_NAME, self.args) | ||
3789 | 861 | assert self.client.root.ref_count > 0 | ||
3790 | 862 | func() | ||
3791 | 863 | |||
3792 | 864 | self.patch(self.client.root, 'timeout_func', fake_timeout_func) | ||
3793 | 865 | self.patch(self.client.root, 'shutdown_func', self._set_called) | ||
3794 | 866 | |||
3795 | 867 | self.client.login(APP_NAME, self.args) | ||
3796 | 868 | self.client.CredentialsFound(APP_NAME, TOKEN) | ||
3797 | 869 | # counter reached 0, timeout_func was called | ||
3798 | 870 | |||
3799 | 871 | self.assertEqual(self._called, False, 'shutdown_func was not called') | ||
3800 | 872 | |||
3801 | 873 | def test_zero_ref_count_after_zero_do_shutdown(self): | ||
3802 | 874 | """If the shutdown was queued, do quit if counter is zero.""" | ||
3803 | 875 | |||
3804 | 876 | def fake_timeout_func(interval, func): | ||
3805 | 877 | """Start a new request when the timer is started.""" | ||
3806 | 878 | assert self.client.root.ref_count == 0 | ||
3807 | 879 | func() | ||
3808 | 880 | |||
3809 | 881 | self.patch(self.client.root, 'timeout_func', fake_timeout_func) | ||
3810 | 882 | self.patch(self.client.root, 'shutdown_func', self._set_called) | ||
3811 | 883 | |||
3812 | 884 | self.client.login(APP_NAME, self.args) | ||
3813 | 885 | self.client.CredentialsFound(APP_NAME, TOKEN) | ||
3814 | 886 | # counter reached 0, timeout_func was called | ||
3815 | 887 | |||
3816 | 888 | self.assertEqual(self._called, ((), {}), 'shutdown_func was called') | ||
3817 | 889 | |||
3818 | 890 | |||
3819 | 891 | class CredentialsManagementFindTestCase(CredentialsManagementTestCase): | ||
3820 | 892 | """Tests for the CredentialsManagement find method.""" | ||
3821 | 893 | |||
3822 | 894 | def test_find_credentials(self): | ||
3823 | 895 | """The credentials are asked and returned in signals.""" | ||
3824 | 896 | self.create_mock_backend().find_credentials() | ||
3825 | 897 | self.mocker.result(defer.succeed(None)) | ||
3826 | 898 | self.mocker.replay() | ||
3827 | 899 | |||
3828 | 900 | self.client.find_credentials(APP_NAME, self.args) | ||
3829 | 901 | self.assert_dbus_method_correct(self.client.find_credentials) | ||
3830 | 902 | |||
3831 | 903 | def test_find_credentials_does_not_block_when_found(self): | ||
3832 | 904 | """Calling find_credentials does not block but return thru signals. | ||
3833 | 905 | |||
3834 | 906 | If the creds are found, CredentialsFound is emitted. | ||
3835 | 907 | |||
3836 | 908 | """ | ||
3837 | 909 | d = defer.Deferred() | ||
3838 | 910 | |||
3839 | 911 | def verify(app_name, creds): | ||
3840 | 912 | """The actual test.""" | ||
3841 | 913 | try: | ||
3842 | 914 | self.assertEqual(app_name, APP_NAME) | ||
3843 | 915 | self.assertEqual(creds, TOKEN) | ||
3844 | 916 | except Exception, e: # pylint: disable=W0703 | ||
3845 | 917 | d.errback(e) | ||
3846 | 918 | else: | ||
3847 | 919 | d.callback(creds) | ||
3848 | 920 | |||
3849 | 921 | self.patch(self.client, 'CredentialsFound', verify) | ||
3850 | 922 | self.patch(self.client, 'CredentialsNotFound', d.errback) | ||
3851 | 923 | |||
3852 | 924 | self.create_mock_backend().find_credentials() | ||
3853 | 925 | self.mocker.result(defer.succeed(TOKEN)) | ||
3854 | 926 | self.mocker.replay() | ||
3855 | 927 | |||
3856 | 928 | self.client.find_credentials(APP_NAME, self.args) | ||
3857 | 929 | return d | ||
3858 | 930 | |||
3859 | 931 | def test_find_credentials_does_not_block_when_not_found(self): | ||
3860 | 932 | """Calling find_credentials does not block but return thru signals. | ||
3861 | 933 | |||
3862 | 934 | If the creds are not found, CredentialsNotFound is emitted. | ||
3863 | 935 | |||
3864 | 936 | """ | ||
3865 | 937 | d = defer.Deferred() | ||
3866 | 938 | |||
3867 | 939 | def verify(app_name): | ||
3868 | 940 | """The actual test.""" | ||
3869 | 941 | try: | ||
3870 | 942 | self.assertEqual(app_name, APP_NAME) | ||
3871 | 943 | except Exception, e: # pylint: disable=W0703 | ||
3872 | 944 | d.errback(e) | ||
3873 | 945 | else: | ||
3874 | 946 | d.callback(app_name) | ||
3875 | 947 | |||
3876 | 948 | self.patch(self.client, 'CredentialsFound', | ||
3877 | 949 | lambda app, creds: d.errback(app)) | ||
3878 | 950 | self.patch(self.client, 'CredentialsNotFound', verify) | ||
3879 | 951 | |||
3880 | 952 | self.create_mock_backend().find_credentials() | ||
3881 | 953 | self.mocker.result(defer.succeed({})) | ||
3882 | 954 | self.mocker.replay() | ||
3883 | 955 | |||
3884 | 956 | self.client.find_credentials(APP_NAME, self.args) | ||
3885 | 957 | return d | ||
3886 | 958 | |||
3887 | 959 | def test_find_credentials_error(self): | ||
3888 | 960 | """If find_credentials fails, CredentialsError is sent.""" | ||
3889 | 961 | d = defer.Deferred() | ||
3890 | 962 | |||
3891 | 963 | def verify(app_name, errdict): | ||
3892 | 964 | """The actual test.""" | ||
3893 | 965 | self.assertEqual(errdict["errtype"], "BlockingSampleException") | ||
3894 | 966 | self.assertEqual(app_name, APP_NAME) | ||
3895 | 967 | d.callback("Ok") | ||
3896 | 968 | |||
3897 | 969 | self.patch(self.client, 'CredentialsFound', | ||
3898 | 970 | lambda app, creds: d.errback(app)) | ||
3899 | 971 | self.patch(self.client, 'CredentialsNotFound', d.errback) | ||
3900 | 972 | self.patch(self.client, 'CredentialsError', verify) | ||
3901 | 973 | |||
3902 | 974 | self.create_mock_backend().find_credentials() | ||
3903 | 975 | self.mocker.result(defer.fail(BlockingSampleException())) | ||
3904 | 976 | self.mocker.replay() | ||
3905 | 977 | |||
3906 | 978 | self.client.find_credentials(APP_NAME, self.args) | ||
3907 | 979 | return d | ||
3908 | 980 | |||
3909 | 981 | def test_find_credentials_sync(self): | ||
3910 | 982 | """The credentials are asked and returned in a blocking call.""" | ||
3911 | 983 | d = defer.Deferred() | ||
3912 | 984 | |||
3913 | 985 | def verify(creds): | ||
3914 | 986 | """The actual test.""" | ||
3915 | 987 | try: | ||
3916 | 988 | self.assertEqual(creds, TOKEN) | ||
3917 | 989 | except Exception, e: # pylint: disable=W0703 | ||
3918 | 990 | d.errback(e) | ||
3919 | 991 | else: | ||
3920 | 992 | d.callback(creds) | ||
3921 | 993 | |||
3922 | 994 | self.create_mock_backend().find_credentials() | ||
3923 | 995 | self.mocker.result(defer.succeed(TOKEN)) | ||
3924 | 996 | self.mocker.replay() | ||
3925 | 997 | |||
3926 | 998 | self.client.find_credentials_sync(APP_NAME, self.args, | ||
3927 | 999 | reply_handler=verify, | ||
3928 | 1000 | error_handler=d.errback) | ||
3929 | 1001 | self.assert_dbus_method_correct(self.client.find_credentials_sync, | ||
3930 | 1002 | out_signature='a{ss}') | ||
3931 | 1003 | return d | ||
3932 | 1004 | |||
3933 | 1005 | def test_find_credentials_sync_error(self): | ||
3934 | 1006 | """If find_credentials_sync fails, error_handler is called.""" | ||
3935 | 1007 | d = defer.Deferred() | ||
3936 | 1008 | |||
3937 | 1009 | def verify(error): | ||
3938 | 1010 | """The actual test.""" | ||
3939 | 1011 | try: | ||
3940 | 1012 | errdict = error.args[0] | ||
3941 | 1013 | self.assertEqual(errdict["errtype"], "BlockingSampleException") | ||
3942 | 1014 | except Exception, e: # pylint: disable=W0703 | ||
3943 | 1015 | d.errback(e) | ||
3944 | 1016 | else: | ||
3945 | 1017 | d.callback("Ok") | ||
3946 | 1018 | |||
3947 | 1019 | self.create_mock_backend().find_credentials() | ||
3948 | 1020 | self.mocker.result(defer.fail(BlockingSampleException())) | ||
3949 | 1021 | self.mocker.replay() | ||
3950 | 1022 | |||
3951 | 1023 | self.client.find_credentials_sync(APP_NAME, self.args, | ||
3952 | 1024 | reply_handler=d.errback, | ||
3953 | 1025 | error_handler=verify) | ||
3954 | 1026 | return d | ||
3955 | 1027 | |||
3956 | 1028 | |||
3957 | 1029 | class CredentialsManagementClearTestCase(CredentialsManagementTestCase): | ||
3958 | 1030 | """Tests for the CredentialsManagement clear method.""" | ||
3959 | 1031 | |||
3960 | 1032 | def test_clear_credentials(self): | ||
3961 | 1033 | """The credentials are removed.""" | ||
3962 | 1034 | self.create_mock_backend().clear_credentials() | ||
3963 | 1035 | self.mocker.result(defer.succeed(APP_NAME)) | ||
3964 | 1036 | self.mocker.replay() | ||
3965 | 1037 | |||
3966 | 1038 | self.client.clear_credentials(APP_NAME, self.args) | ||
3967 | 1039 | self.assert_dbus_method_correct(self.client.clear_credentials) | ||
3968 | 1040 | |||
3969 | 1041 | def test_clear_credentials_does_not_block(self): | ||
3970 | 1042 | """Calling clear_credentials does not block but return thru signals.""" | ||
3971 | 1043 | d = defer.Deferred() | ||
3972 | 1044 | |||
3973 | 1045 | def verify(app_name): | ||
3974 | 1046 | """The actual test.""" | ||
3975 | 1047 | try: | ||
3976 | 1048 | self.assertEqual(app_name, APP_NAME) | ||
3977 | 1049 | except Exception, e: # pylint: disable=W0703 | ||
3978 | 1050 | d.errback(e) | ||
3979 | 1051 | else: | ||
3980 | 1052 | d.callback(app_name) | ||
3981 | 1053 | |||
3982 | 1054 | self.patch(self.client, 'CredentialsCleared', verify) | ||
3983 | 1055 | self.patch(self.client, 'CredentialsError', | ||
3984 | 1056 | lambda app, err: d.errback(app)) | ||
3985 | 1057 | |||
3986 | 1058 | self.create_mock_backend().clear_credentials() | ||
3987 | 1059 | self.mocker.result(defer.succeed(APP_NAME)) | ||
3988 | 1060 | self.mocker.replay() | ||
3989 | 1061 | |||
3990 | 1062 | self.client.clear_credentials(APP_NAME, self.args) | ||
3991 | 1063 | return d | ||
3992 | 1064 | |||
3993 | 1065 | def test_clear_credentials_error(self): | ||
3994 | 1066 | """If clear_credentials fails, CredentialsError is sent.""" | ||
3995 | 1067 | d = defer.Deferred() | ||
3996 | 1068 | |||
3997 | 1069 | def verify(app_name, errdict): | ||
3998 | 1070 | """The actual test.""" | ||
3999 | 1071 | self.assertEqual(errdict["errtype"], "BlockingSampleException") | ||
4000 | 1072 | self.assertEqual(app_name, APP_NAME) | ||
4001 | 1073 | d.callback("Ok") | ||
4002 | 1074 | |||
4003 | 1075 | self.patch(self.client, 'CredentialsCleared', d.errback) | ||
4004 | 1076 | self.patch(self.client, 'CredentialsError', verify) | ||
4005 | 1077 | |||
4006 | 1078 | self.create_mock_backend().clear_credentials() | ||
4007 | 1079 | self.mocker.result(defer.fail(BlockingSampleException())) | ||
4008 | 1080 | self.mocker.replay() | ||
4009 | 1081 | |||
4010 | 1082 | self.client.clear_credentials(APP_NAME, self.args) | ||
4011 | 1083 | return d | ||
4012 | 1084 | |||
4013 | 1085 | |||
4014 | 1086 | class CredentialsManagementStoreTestCase(CredentialsManagementTestCase): | ||
4015 | 1087 | """Tests for the CredentialsManagement store method.""" | ||
4016 | 1088 | |||
4017 | 1089 | def test_store_credentials(self): | ||
4018 | 1090 | """The credentials are stored and the outcome is a signal.""" | ||
4019 | 1091 | self.create_mock_backend().store_credentials(TOKEN) | ||
4020 | 1092 | self.mocker.result(defer.succeed(APP_NAME)) | ||
4021 | 1093 | self.mocker.replay() | ||
4022 | 1094 | |||
4023 | 1095 | self.client.store_credentials(APP_NAME, TOKEN) | ||
4024 | 1096 | self.assert_dbus_method_correct(self.client.store_credentials) | ||
4025 | 1097 | |||
4026 | 1098 | def test_store_credentials_does_not_block(self): | ||
4027 | 1099 | """Calling store_credentials does not block but return thru signals. | ||
4028 | 1100 | |||
4029 | 1101 | If the creds are stored, CredentialsStored is emitted. | ||
4030 | 1102 | |||
4031 | 1103 | """ | ||
4032 | 1104 | d = defer.Deferred() | ||
4033 | 1105 | |||
4034 | 1106 | def verify(app_name): | ||
4035 | 1107 | """The actual test.""" | ||
4036 | 1108 | try: | ||
4037 | 1109 | self.assertEqual(app_name, APP_NAME) | ||
4038 | 1110 | except Exception, e: # pylint: disable=W0703 | ||
4039 | 1111 | d.errback(e) | ||
4040 | 1112 | else: | ||
4041 | 1113 | d.callback(app_name) | ||
4042 | 1114 | |||
4043 | 1115 | self.patch(self.client, 'CredentialsStored', verify) | ||
4044 | 1116 | self.patch(self.client, 'CredentialsError', | ||
4045 | 1117 | lambda app, err: d.errback(app)) | ||
4046 | 1118 | |||
4047 | 1119 | self.create_mock_backend().store_credentials(TOKEN) | ||
4048 | 1120 | self.mocker.result(defer.succeed(APP_NAME)) | ||
4049 | 1121 | self.mocker.replay() | ||
4050 | 1122 | |||
4051 | 1123 | self.client.store_credentials(APP_NAME, TOKEN) | ||
4052 | 1124 | return d | ||
4053 | 1125 | |||
4054 | 1126 | def test_store_credentials_error(self): | ||
4055 | 1127 | """If store_credentials fails, CredentialsError is sent.""" | ||
4056 | 1128 | d = defer.Deferred() | ||
4057 | 1129 | |||
4058 | 1130 | def verify(app_name, errdict): | ||
4059 | 1131 | """The actual test.""" | ||
4060 | 1132 | self.assertEqual(errdict["errtype"], "BlockingSampleException") | ||
4061 | 1133 | self.assertEqual(app_name, APP_NAME) | ||
4062 | 1134 | d.callback("Ok") | ||
4063 | 1135 | |||
4064 | 1136 | self.patch(self.client, 'CredentialsStored', d.errback) | ||
4065 | 1137 | self.patch(self.client, 'CredentialsError', verify) | ||
4066 | 1138 | |||
4067 | 1139 | self.create_mock_backend().store_credentials(TOKEN) | ||
4068 | 1140 | self.mocker.result(defer.fail(BlockingSampleException())) | ||
4069 | 1141 | self.mocker.replay() | ||
4070 | 1142 | |||
4071 | 1143 | self.client.store_credentials(APP_NAME, TOKEN) | ||
4072 | 1144 | return d | ||
4073 | 1145 | |||
4074 | 1146 | |||
4075 | 1147 | class CredentialsManagementOpsTestCase(CredentialsManagementTestCase): | ||
4076 | 1148 | """Tests for the CredentialsManagement login/register methods.""" | ||
4077 | 1149 | |||
4078 | 1150 | @defer.inlineCallbacks | ||
4079 | 1151 | def setUp(self): | ||
4080 | 1152 | yield super(CredentialsManagementOpsTestCase, self).setUp() | ||
4081 | 1153 | self.args = dict((k, str(v)) for k, v in self.base_args.iteritems()) | ||
4082 | 1154 | self.cred_args = self.base_args.copy() | ||
4083 | 1155 | self.cred_args[SUCCESS_CB_KEY] = self.client.CredentialsFound | ||
4084 | 1156 | self.cred_args[ERROR_CB_KEY] = self.client.CredentialsError | ||
4085 | 1157 | self.cred_args[DENIAL_CB_KEY] = self.client.AuthorizationDenied | ||
4086 | 1158 | |||
4087 | 1159 | def test_register(self): | ||
4088 | 1160 | """The registration is correct.""" | ||
4089 | 1161 | self.create_mock_backend().register() | ||
4090 | 1162 | self.mocker.replay() | ||
4091 | 1163 | |||
4092 | 1164 | self.client.register(APP_NAME, self.args) | ||
4093 | 1165 | self.assert_dbus_method_correct(self.client.register) | ||
4094 | 1166 | |||
4095 | 1167 | def test_login(self): | ||
4096 | 1168 | """The login is correct.""" | ||
4097 | 1169 | self.create_mock_backend().login() | ||
4098 | 1170 | self.mocker.replay() | ||
4099 | 1171 | |||
4100 | 1172 | self.client.login(APP_NAME, self.args) | ||
4101 | 1173 | self.assert_dbus_method_correct(self.client.login) | ||
4102 | 1174 | |||
4103 | 1175 | def test_login_email_password(self): | ||
4104 | 1176 | """The login_email_password is correct.""" | ||
4105 | 1177 | self.create_mock_backend().login_email_password(email=EMAIL, | ||
4106 | 1178 | password=PASSWORD) | ||
4107 | 1179 | self.mocker.replay() | ||
4108 | 1180 | |||
4109 | 1181 | self.args['email'] = EMAIL | ||
4110 | 1182 | self.args['password'] = PASSWORD | ||
4111 | 1183 | self.client.login_email_password(APP_NAME, self.args) | ||
4112 | 1184 | self.assert_dbus_method_correct(self.client.login_email_password) | ||
4113 | 1185 | |||
4114 | 1186 | |||
4115 | 1187 | class CredentialsManagementParamsTestCase(CredentialsManagementOpsTestCase): | ||
4116 | 1188 | """Tests for the CredentialsManagement extra parameters handling.""" | ||
4117 | 1189 | |||
4118 | 1190 | @defer.inlineCallbacks | ||
4119 | 1191 | def setUp(self): | ||
4120 | 1192 | yield super(CredentialsManagementParamsTestCase, self).setUp() | ||
4121 | 1193 | self.args['dummy'] = 'nothing useful' | ||
4122 | 1194 | |||
4123 | 1195 | |||
4124 | 1196 | class CredentialsManagementSignalsTestCase(TestCase): | ||
4125 | 1197 | """Tests for the CredentialsManagement DBus signals.""" | ||
4126 | 1198 | |||
4127 | 1199 | @defer.inlineCallbacks | ||
4128 | 1200 | def setUp(self): | ||
4129 | 1201 | """Set up.""" | ||
4130 | 1202 | yield super(CredentialsManagementSignalsTestCase, self).setUp() | ||
4131 | 1203 | self.client = CredentialsManagement(timeout_func=lambda *a: None, | ||
4132 | 1204 | shutdown_func=lambda *a: None) | ||
4133 | 1205 | |||
4134 | 1206 | self.memento = MementoHandler() | ||
4135 | 1207 | self.memento.setLevel(logging.DEBUG) | ||
4136 | 1208 | ubuntu_sso.main.linux.logger.addHandler(self.memento) | ||
4137 | 1209 | self.addCleanup(ubuntu_sso.main.linux.logger.removeHandler, | ||
4138 | 1210 | self.memento) | ||
4139 | 1211 | |||
4140 | 1212 | def assert_dbus_signal_correct(self, signal, signature): | ||
4141 | 1213 | """Check that 'signal' is a dbus signal with proper 'signature'.""" | ||
4142 | 1214 | self.assertTrue(signal._dbus_is_signal) | ||
4143 | 1215 | self.assertEqual(signal._dbus_interface, DBUS_CREDENTIALS_IFACE) | ||
4144 | 1216 | self.assertEqual(signal._dbus_signature, signature) | ||
4145 | 1217 | |||
4146 | 1218 | def test_credentials_found(self): | ||
4147 | 1219 | """The CredentialsFound signal.""" | ||
4148 | 1220 | self.client.CredentialsFound(APP_NAME, TOKEN) | ||
4149 | 1221 | msgs = (self.client.__class__.__name__, | ||
4150 | 1222 | self.client.CredentialsFound.__name__, APP_NAME) | ||
4151 | 1223 | self.assertTrue(self.memento.check_info(*msgs)) | ||
4152 | 1224 | |||
4153 | 1225 | msg = 'credentials must not be logged (found %r in log).' | ||
4154 | 1226 | for val in TOKEN.itervalues(): | ||
4155 | 1227 | self.assertFalse(self.memento.check_info(val), msg % val) | ||
4156 | 1228 | |||
4157 | 1229 | self.assert_dbus_signal_correct(self.client.CredentialsFound, 'sa{ss}') | ||
4158 | 1230 | |||
4159 | 1231 | def test_credentials_not_found(self): | ||
4160 | 1232 | """The CredentialsNotFound signal.""" | ||
4161 | 1233 | self.client.CredentialsNotFound(APP_NAME) | ||
4162 | 1234 | msgs = (self.client.__class__.__name__, | ||
4163 | 1235 | self.client.CredentialsNotFound.__name__, APP_NAME) | ||
4164 | 1236 | self.assertTrue(self.memento.check_info(*msgs)) | ||
4165 | 1237 | self.assert_dbus_signal_correct(self.client.CredentialsNotFound, 's') | ||
4166 | 1238 | |||
4167 | 1239 | def test_credentials_cleared(self): | ||
4168 | 1240 | """The CredentialsCleared signal.""" | ||
4169 | 1241 | self.client.CredentialsCleared(APP_NAME) | ||
4170 | 1242 | msgs = (self.client.__class__.__name__, | ||
4171 | 1243 | self.client.CredentialsCleared.__name__, APP_NAME) | ||
4172 | 1244 | self.assertTrue(self.memento.check_info(*msgs)) | ||
4173 | 1245 | |||
4174 | 1246 | self.assert_dbus_signal_correct(self.client.CredentialsCleared, 's') | ||
4175 | 1247 | |||
4176 | 1248 | def test_credentials_stored(self): | ||
4177 | 1249 | """The CredentialsStored signal.""" | ||
4178 | 1250 | self.client.CredentialsStored(APP_NAME) | ||
4179 | 1251 | msgs = (self.client.__class__.__name__, | ||
4180 | 1252 | self.client.CredentialsStored.__name__, APP_NAME) | ||
4181 | 1253 | self.assertTrue(self.memento.check_info(*msgs)) | ||
4182 | 1254 | |||
4183 | 1255 | self.assert_dbus_signal_correct(self.client.CredentialsStored, 's') | ||
4184 | 1256 | |||
4185 | 1257 | def test_credentials_error(self): | ||
4186 | 1258 | """The CredentialsError signal.""" | ||
4187 | 1259 | error = {'error_message': 'failed!', 'detailed error': 'yadda yadda'} | ||
4188 | 1260 | self.client.CredentialsError(APP_NAME, error) | ||
4189 | 1261 | msgs = (self.client.__class__.__name__, | ||
4190 | 1262 | self.client.CredentialsError.__name__, | ||
4191 | 1263 | APP_NAME, str(error)) | ||
4192 | 1264 | self.assertTrue(self.memento.check_error(*msgs)) | ||
4193 | 1265 | |||
4194 | 1266 | self.assert_dbus_signal_correct(self.client.CredentialsError, 'sa{ss}') | ||
4195 | 1267 | |||
4196 | 1268 | def test_authorization_denied(self): | ||
4197 | 1269 | """The AuthorizationDenied signal.""" | ||
4198 | 1270 | self.client.AuthorizationDenied(APP_NAME) | ||
4199 | 1271 | msgs = (self.client.__class__.__name__, | ||
4200 | 1272 | self.client.AuthorizationDenied.__name__, APP_NAME) | ||
4201 | 1273 | self.assertTrue(self.memento.check_info(*msgs)) | ||
4202 | 1274 | |||
4203 | 1275 | self.assert_dbus_signal_correct(self.client.AuthorizationDenied, 's') | ||
4204 | 1276 | 0 | ||
4205 | === modified file 'ubuntu_sso/main/tests/test_windows.py' | |||
4206 | --- ubuntu_sso/main/tests/test_windows.py 2011-12-20 17:33:13 +0000 | |||
4207 | +++ ubuntu_sso/main/tests/test_windows.py 2012-01-17 19:42:46 +0000 | |||
4208 | @@ -1,6 +1,4 @@ | |||
4209 | 1 | # -*- coding: utf-8 -*- | 1 | # -*- coding: utf-8 -*- |
4210 | 2 | # Authors: Manuel de la Pena <manuel@canonical.com> | ||
4211 | 3 | # Alejandro J. Cura <alecu@canonical.com> | ||
4212 | 4 | # | 2 | # |
4213 | 5 | # Copyright 2011 Canonical Ltd. | 3 | # Copyright 2011 Canonical Ltd. |
4214 | 6 | # | 4 | # |
4215 | @@ -15,932 +13,16 @@ | |||
4216 | 15 | # | 13 | # |
4217 | 16 | # You should have received a copy of the GNU General Public License along | 14 | # You should have received a copy of the GNU General Public License along |
4218 | 17 | # with this program. If not, see <http://www.gnu.org/licenses/>. | 15 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
4220 | 18 | """Windows tests.""" | 16 | """Windows specific tests for the main module.""" |
4221 | 19 | 17 | ||
4222 | 20 | # pylint: disable=F0401 | 18 | # pylint: disable=F0401 |
4223 | 21 | from _winreg import REG_SZ | 19 | from _winreg import REG_SZ |
4224 | 22 | 20 | ||
4225 | 23 | from mocker import MATCH, Mocker, MockerTestCase | ||
4226 | 24 | |||
4227 | 25 | from twisted.internet import defer, reactor | ||
4228 | 26 | from twisted.trial.unittest import TestCase | ||
4229 | 27 | from twisted.spread.pb import ( | ||
4230 | 28 | DeadReferenceError, | ||
4231 | 29 | PBClientFactory, | ||
4232 | 30 | PBServerFactory, | ||
4233 | 31 | Broker, | ||
4234 | 32 | ) | ||
4235 | 33 | from ubuntu_sso import main | ||
4236 | 34 | from ubuntu_sso.main import windows | 21 | from ubuntu_sso.main import windows |
4237 | 35 | from ubuntu_sso.main.windows import ( | ||
4238 | 36 | signal, | ||
4239 | 37 | CredentialsManagement, | ||
4240 | 38 | CredentialsManagementClient, | ||
4241 | 39 | LOCALHOST, | ||
4242 | 40 | SignalBroadcaster, | ||
4243 | 41 | SSOLogin, | ||
4244 | 42 | SSOLoginClient, | ||
4245 | 43 | UbuntuSSORoot, | ||
4246 | 44 | UbuntuSSOClient, | ||
4247 | 45 | get_sso_pb_port, | ||
4248 | 46 | ) | ||
4249 | 47 | |||
4250 | 48 | # because we are using twisted we have java like names C0103 and | ||
4251 | 49 | # the issues that mocker brings with it like W0104 | ||
4252 | 50 | # pylint: disable=C0103,W0104,E1101,W0201 | ||
4253 | 51 | |||
4254 | 52 | |||
4255 | 53 | class SaveProtocolServerFactory(PBServerFactory): | ||
4256 | 54 | """A PBServerFactory that saves the latest connected client.""" | ||
4257 | 55 | |||
4258 | 56 | protocolInstance = None | ||
4259 | 57 | |||
4260 | 58 | def clientConnectionMade(self, protocol): | ||
4261 | 59 | """Keep track of the given protocol.""" | ||
4262 | 60 | self.protocolInstance = protocol | ||
4263 | 61 | |||
4264 | 62 | |||
4265 | 63 | class SaveClientFactory(PBClientFactory): | ||
4266 | 64 | """Client Factory that knows when we disconnected.""" | ||
4267 | 65 | |||
4268 | 66 | def __init__(self, connected_d, disconnected_d): | ||
4269 | 67 | """Create a new instance.""" | ||
4270 | 68 | PBClientFactory.__init__(self) | ||
4271 | 69 | self.connected_d = connected_d | ||
4272 | 70 | self.disconnected_d = disconnected_d | ||
4273 | 71 | |||
4274 | 72 | def clientConnectionMade(self, broker): | ||
4275 | 73 | """Connection made.""" | ||
4276 | 74 | PBClientFactory.clientConnectionMade(self, broker) | ||
4277 | 75 | self.connected_d.callback(True) | ||
4278 | 76 | |||
4279 | 77 | def clientConnectionLost(self, connector, reason, reconnecting=0): | ||
4280 | 78 | """Connection lost.""" | ||
4281 | 79 | self.disconnected_d.callback(True) | ||
4282 | 80 | |||
4283 | 81 | |||
4284 | 82 | class ServerProtocol(Broker): | ||
4285 | 83 | """Server protocol that allows us to clean the tests.""" | ||
4286 | 84 | |||
4287 | 85 | def connectionLost(self, *a): | ||
4288 | 86 | self.factory.onConnectionLost.callback(self) | ||
4289 | 87 | |||
4290 | 88 | |||
4291 | 89 | class ConnectedTestCase(TestCase): | ||
4292 | 90 | """Base test case with a client and a server.""" | ||
4293 | 91 | |||
4294 | 92 | @defer.inlineCallbacks | ||
4295 | 93 | def setUp(self): | ||
4296 | 94 | """Set up for the tests.""" | ||
4297 | 95 | yield super(ConnectedTestCase, self).setUp() | ||
4298 | 96 | self.server_disconnected = defer.Deferred() | ||
4299 | 97 | self.client_disconnected = defer.Deferred() | ||
4300 | 98 | self.listener = None | ||
4301 | 99 | self.connector = None | ||
4302 | 100 | self.server_factory = None | ||
4303 | 101 | self.client_factory = None | ||
4304 | 102 | |||
4305 | 103 | def setup_client_server(self, sso_root): | ||
4306 | 104 | """Set tests.""" | ||
4307 | 105 | port = get_sso_pb_port() | ||
4308 | 106 | self.listener = self._listen_server(sso_root, self.server_disconnected, | ||
4309 | 107 | port) | ||
4310 | 108 | connected = defer.Deferred() | ||
4311 | 109 | self.connector = self._connect_client(connected, | ||
4312 | 110 | self.client_disconnected, port) | ||
4313 | 111 | self.addCleanup(self.teardown_client_server) | ||
4314 | 112 | return connected | ||
4315 | 113 | |||
4316 | 114 | def _listen_server(self, sso_root, d, port): | ||
4317 | 115 | """Start listenting.""" | ||
4318 | 116 | self.server_factory = SaveProtocolServerFactory(sso_root) | ||
4319 | 117 | self.server_factory.onConnectionLost = d | ||
4320 | 118 | self.server_factory.protocol = ServerProtocol | ||
4321 | 119 | return reactor.listenTCP(port, self.server_factory) | ||
4322 | 120 | |||
4323 | 121 | def _connect_client(self, d1, d2, port): | ||
4324 | 122 | """Connect client.""" | ||
4325 | 123 | self.client_factory = SaveClientFactory(d1, d2) | ||
4326 | 124 | return reactor.connectTCP(LOCALHOST, port, self.client_factory) | ||
4327 | 125 | |||
4328 | 126 | def teardown_client_server(self): | ||
4329 | 127 | """Clean resources.""" | ||
4330 | 128 | self.connector.disconnect() | ||
4331 | 129 | d = defer.maybeDeferred(self.listener.stopListening) | ||
4332 | 130 | return defer.gatherResults([d, self.client_disconnected, | ||
4333 | 131 | self.server_disconnected]) | ||
4334 | 132 | |||
4335 | 133 | |||
4336 | 134 | class FakeDecoratedObject(object): | ||
4337 | 135 | """An object that has decorators.""" | ||
4338 | 136 | |||
4339 | 137 | def __init__(self): | ||
4340 | 138 | """Create a new instance.""" | ||
4341 | 139 | super(FakeDecoratedObject, self).__init__() | ||
4342 | 140 | |||
4343 | 141 | @signal | ||
4344 | 142 | def on_no_args(self): | ||
4345 | 143 | """Get no args passwed.""" | ||
4346 | 144 | |||
4347 | 145 | @signal | ||
4348 | 146 | def on_just_args(self, *args): | ||
4349 | 147 | """Just get args.""" | ||
4350 | 148 | |||
4351 | 149 | @signal | ||
4352 | 150 | def on_just_kwargs(self, **kwargs): | ||
4353 | 151 | """Just get kwargs.""" | ||
4354 | 152 | |||
4355 | 153 | @signal | ||
4356 | 154 | def on_both_args(self, *args, **kwargs): | ||
4357 | 155 | """Both args.""" | ||
4358 | 156 | |||
4359 | 157 | |||
4360 | 158 | class TestException(Exception): | ||
4361 | 159 | """A test exception.""" | ||
4362 | 160 | |||
4363 | 161 | @classmethod | ||
4364 | 162 | def fail(cls, *args): | ||
4365 | 163 | """Raise this exception.""" | ||
4366 | 164 | raise cls(*args) | ||
4367 | 165 | |||
4368 | 166 | |||
4369 | 167 | # pylint: disable=W0201 | ||
4370 | 168 | class SignalTestCase(MockerTestCase): | ||
4371 | 169 | """Test the signal decorator.""" | ||
4372 | 170 | |||
4373 | 171 | @defer.inlineCallbacks | ||
4374 | 172 | def setUp(self): | ||
4375 | 173 | yield super(SignalTestCase, self).setUp() | ||
4376 | 174 | self.fake_object = FakeDecoratedObject() | ||
4377 | 175 | self.cb = self.mocker.mock() | ||
4378 | 176 | |||
4379 | 177 | def test_no_args(self): | ||
4380 | 178 | """Test when the cb should have no args.""" | ||
4381 | 179 | self.fake_object.on_no_args_cb = self.cb | ||
4382 | 180 | self.cb() | ||
4383 | 181 | self.mocker.replay() | ||
4384 | 182 | self.fake_object.on_no_args() | ||
4385 | 183 | |||
4386 | 184 | def test_just_args(self): | ||
4387 | 185 | """Test when the cb just has *args""" | ||
4388 | 186 | first = 'first' | ||
4389 | 187 | second = 'second' | ||
4390 | 188 | self.fake_object.on_just_args_cb = self.cb | ||
4391 | 189 | self.cb(first, second) | ||
4392 | 190 | self.mocker.replay() | ||
4393 | 191 | self.fake_object.on_just_args(first, second) | ||
4394 | 192 | |||
4395 | 193 | def test_just_kwargs(self): | ||
4396 | 194 | """Test when the cb just has kwargs.""" | ||
4397 | 195 | first = 'first' | ||
4398 | 196 | second = 'second' | ||
4399 | 197 | self.fake_object.on_just_kwargs_cb = self.cb | ||
4400 | 198 | self.cb(first=first, second=second) | ||
4401 | 199 | self.mocker.replay() | ||
4402 | 200 | self.fake_object.on_just_kwargs(first=first, second=second) | ||
4403 | 201 | |||
4404 | 202 | def test_just_kwargs_empty(self): | ||
4405 | 203 | """Test when the cb just has kwargs.""" | ||
4406 | 204 | self.fake_object.on_just_kwargs_cb = self.cb | ||
4407 | 205 | self.cb() | ||
4408 | 206 | self.mocker.replay() | ||
4409 | 207 | self.fake_object.on_just_kwargs() | ||
4410 | 208 | |||
4411 | 209 | def test_both_args(self): | ||
4412 | 210 | """Test with args and kwargs.""" | ||
4413 | 211 | first = 'first' | ||
4414 | 212 | second = 'second' | ||
4415 | 213 | self.fake_object.on_both_args_cb = self.cb | ||
4416 | 214 | self.cb(first, second, first=first, second=second) | ||
4417 | 215 | self.mocker.replay() | ||
4418 | 216 | self.fake_object.on_both_args(first, second, first=first, | ||
4419 | 217 | second=second) | ||
4420 | 218 | |||
4421 | 219 | def test_both_args_no_kwargs(self): | ||
4422 | 220 | """Test with args and kwargs.""" | ||
4423 | 221 | first = 'first' | ||
4424 | 222 | second = 'second' | ||
4425 | 223 | self.fake_object.on_both_args_cb = self.cb | ||
4426 | 224 | self.cb(first, second) | ||
4427 | 225 | self.mocker.replay() | ||
4428 | 226 | self.fake_object.on_both_args(first, second) | ||
4429 | 227 | |||
4430 | 228 | def test_both_args_no_args(self): | ||
4431 | 229 | """Test with args and kwargs.""" | ||
4432 | 230 | first = 'first' | ||
4433 | 231 | second = 'second' | ||
4434 | 232 | self.fake_object.on_both_args_cb = self.cb | ||
4435 | 233 | self.cb(first=first, second=second) | ||
4436 | 234 | self.mocker.replay() | ||
4437 | 235 | self.fake_object.on_both_args(first=first, second=second) | ||
4438 | 236 | # pylint: enable=W0201 | ||
4439 | 237 | |||
4440 | 238 | |||
4441 | 239 | class FakeDeadRemoteClient(object): | ||
4442 | 240 | """A fake dead remote client.""" | ||
4443 | 241 | |||
4444 | 242 | def callRemote(self, signal_name, *args, **kwargs): | ||
4445 | 243 | """Fails with DeadReferenceError.""" | ||
4446 | 244 | raise DeadReferenceError("Calling Stale Broker") | ||
4447 | 245 | |||
4448 | 246 | |||
4449 | 247 | class FakeAliveRemoteClient(object): | ||
4450 | 248 | """A fake alive remote client.""" | ||
4451 | 249 | |||
4452 | 250 | def __init__(self): | ||
4453 | 251 | self.called = False | ||
4454 | 252 | |||
4455 | 253 | def callRemote(self, signal_name, *args, **kwargs): | ||
4456 | 254 | """Returns a succeed.""" | ||
4457 | 255 | self.called = True | ||
4458 | 256 | return defer.succeed(None) | ||
4459 | 257 | |||
4460 | 258 | |||
4461 | 259 | class SignalBroadcasterTestCase(TestCase): | ||
4462 | 260 | """Test the SignalBroadcaster class.""" | ||
4463 | 261 | |||
4464 | 262 | def test_emit_signal_dead_reference(self): | ||
4465 | 263 | """Test dead reference while emiting the signal.""" | ||
4466 | 264 | fake_remote_client = FakeDeadRemoteClient() | ||
4467 | 265 | sb = SignalBroadcaster() | ||
4468 | 266 | sb.remote_register_to_signals(fake_remote_client) | ||
4469 | 267 | self.assertIn(fake_remote_client, sb.clients) | ||
4470 | 268 | sb.emit_signal("sample_signal") | ||
4471 | 269 | self.assertNotIn(fake_remote_client, sb.clients) | ||
4472 | 270 | |||
4473 | 271 | def test_emit_signal_some_dead_some_not(self): | ||
4474 | 272 | """Test a clean reference after a dead one.""" | ||
4475 | 273 | fake_dead_remote = FakeDeadRemoteClient() | ||
4476 | 274 | fake_alive_remote = FakeAliveRemoteClient() | ||
4477 | 275 | sb = SignalBroadcaster() | ||
4478 | 276 | sb.remote_register_to_signals(fake_dead_remote) | ||
4479 | 277 | sb.remote_register_to_signals(fake_alive_remote) | ||
4480 | 278 | sb.emit_signal("sample_signal") | ||
4481 | 279 | self.assertTrue(fake_alive_remote.called, "The alive must be called.") | ||
4482 | 280 | |||
4483 | 281 | |||
4484 | 282 | class SignalHandlingTestCase(TestCase): | ||
4485 | 283 | """Base for test suites that deal with IPC signal handling.""" | ||
4486 | 284 | |||
4487 | 285 | timeout = 3 | ||
4488 | 286 | signal_names = [] | ||
4489 | 287 | first_signal = None | ||
4490 | 288 | |||
4491 | 289 | def create_handler(self, d): | ||
4492 | 290 | """Create a handler in a new namespace.""" | ||
4493 | 291 | return lambda *args: d.callback(args) | ||
4494 | 292 | |||
4495 | 293 | def install_handlers(self, client): | ||
4496 | 294 | """Install the signal handlers.""" | ||
4497 | 295 | signal_handlers = [] | ||
4498 | 296 | |||
4499 | 297 | for signal_name in self.signal_names: | ||
4500 | 298 | d = defer.Deferred() | ||
4501 | 299 | handler = self.create_handler(d) | ||
4502 | 300 | handler_name = "on_%s_cb" % signal_name | ||
4503 | 301 | setattr(client, handler_name, handler) | ||
4504 | 302 | signal_handlers.append(d) | ||
4505 | 303 | self.first_signal = defer.DeferredList(signal_handlers, | ||
4506 | 304 | fireOnOneCallback=True, | ||
4507 | 305 | fireOnOneErrback=True) | ||
4508 | 306 | |||
4509 | 307 | @defer.inlineCallbacks | ||
4510 | 308 | def assert_fired(self, name, *args): | ||
4511 | 309 | """Assert that the given signal was fired.""" | ||
4512 | 310 | signal_args, signal_index = yield self.first_signal | ||
4513 | 311 | self.assertEqual(args, signal_args[:len(args)]) | ||
4514 | 312 | self.assertEqual(name, self.signal_names[signal_index]) | ||
4515 | 313 | |||
4516 | 314 | |||
4517 | 315 | class SSOLoginTestCase(ConnectedTestCase, SignalHandlingTestCase): | ||
4518 | 316 | """Test the login class.""" | ||
4519 | 317 | |||
4520 | 318 | signal_names = [ | ||
4521 | 319 | 'captcha_generated', | ||
4522 | 320 | 'captcha_generation_error', | ||
4523 | 321 | 'user_registered', | ||
4524 | 322 | 'user_registration_error', | ||
4525 | 323 | 'logged_in', | ||
4526 | 324 | 'login_error', | ||
4527 | 325 | 'user_not_validated', | ||
4528 | 326 | 'email_validated', | ||
4529 | 327 | 'email_validation_error', | ||
4530 | 328 | 'password_reset_token_sent', | ||
4531 | 329 | 'password_reset_error', | ||
4532 | 330 | 'password_changed', | ||
4533 | 331 | 'password_change_error', | ||
4534 | 332 | ] | ||
4535 | 333 | |||
4536 | 334 | @defer.inlineCallbacks | ||
4537 | 335 | def setUp(self): | ||
4538 | 336 | """Setup tests.""" | ||
4539 | 337 | yield super(SSOLoginTestCase, self).setUp() | ||
4540 | 338 | self.mocker = Mocker() | ||
4541 | 339 | self.root = self.mocker.mock() | ||
4542 | 340 | self.login = SSOLogin(None) | ||
4543 | 341 | # start pb | ||
4544 | 342 | self.sso_root = UbuntuSSORoot(sso_login=self.login) | ||
4545 | 343 | # pylint: disable=E1101 | ||
4546 | 344 | yield self.setup_client_server(self.sso_root) | ||
4547 | 345 | self.client = yield self._get_client() | ||
4548 | 346 | # pylint: enable=E1101 | ||
4549 | 347 | |||
4550 | 348 | @defer.inlineCallbacks | ||
4551 | 349 | def _get_client(self): | ||
4552 | 350 | """Get the client.""" | ||
4553 | 351 | # request the remote object and create a client | ||
4554 | 352 | root = yield self.client_factory.getRootObject() | ||
4555 | 353 | remote = yield root.callRemote('get_sso_login') | ||
4556 | 354 | client = SSOLoginClient(remote) | ||
4557 | 355 | yield client.register_to_signals() | ||
4558 | 356 | self.addCleanup(client.unregister_to_signals) | ||
4559 | 357 | |||
4560 | 358 | self.install_handlers(client) | ||
4561 | 359 | defer.returnValue(client) | ||
4562 | 360 | |||
4563 | 361 | @defer.inlineCallbacks | ||
4564 | 362 | def test_emit_captcha_generated(self): | ||
4565 | 363 | """Test that the cb was called.""" | ||
4566 | 364 | app_name = 'app' | ||
4567 | 365 | result = 'result' | ||
4568 | 366 | |||
4569 | 367 | self.login.emit_captcha_generated(app_name, result) | ||
4570 | 368 | yield self.assert_fired("captcha_generated", app_name, result) | ||
4571 | 369 | |||
4572 | 370 | @defer.inlineCallbacks | ||
4573 | 371 | def test_emit_captcha_generation_error(self): | ||
4574 | 372 | """Test that the cb was called.""" | ||
4575 | 373 | app_name = 'app' | ||
4576 | 374 | filename = 'file' | ||
4577 | 375 | |||
4578 | 376 | self.patch(self.login.root.processor, "generate_captcha", | ||
4579 | 377 | TestException.fail) | ||
4580 | 378 | self.login.generate_captcha(app_name, filename) | ||
4581 | 379 | yield self.assert_fired("captcha_generation_error", app_name) | ||
4582 | 380 | |||
4583 | 381 | @defer.inlineCallbacks | ||
4584 | 382 | def test_generate_captcha(self): | ||
4585 | 383 | """Test the call from the client.""" | ||
4586 | 384 | app_name = 'app' | ||
4587 | 385 | filename = 'file' | ||
4588 | 386 | self.login.root = self.root | ||
4589 | 387 | |||
4590 | 388 | self.root.generate_captcha(app_name, filename, | ||
4591 | 389 | self.login.emit_captcha_generated, | ||
4592 | 390 | self.login.emit_captcha_generation_error) | ||
4593 | 391 | self.mocker.replay() | ||
4594 | 392 | yield self.client.generate_captcha(app_name, filename) | ||
4595 | 393 | yield self.client.unregister_to_signals() | ||
4596 | 394 | self.mocker.verify() | ||
4597 | 395 | |||
4598 | 396 | @defer.inlineCallbacks | ||
4599 | 397 | def test_emit_user_registered(self): | ||
4600 | 398 | """Test that the cb was called.""" | ||
4601 | 399 | app_name = 'app' | ||
4602 | 400 | result = 'result' | ||
4603 | 401 | |||
4604 | 402 | self.login.emit_user_registered(app_name, result) | ||
4605 | 403 | yield self.assert_fired("user_registered", app_name, result) | ||
4606 | 404 | |||
4607 | 405 | @defer.inlineCallbacks | ||
4608 | 406 | def test_emit_user_registration_error(self): | ||
4609 | 407 | """Test that the cb was called.""" | ||
4610 | 408 | app_name = 'app' | ||
4611 | 409 | |||
4612 | 410 | self.patch(self.login.root.processor, "register_user", | ||
4613 | 411 | TestException.fail) | ||
4614 | 412 | self.login.register_user(app_name, "email", "password", "name", | ||
4615 | 413 | "captcha_id", "captcha_solution") | ||
4616 | 414 | yield self.assert_fired("user_registration_error", app_name) | ||
4617 | 415 | |||
4618 | 416 | @defer.inlineCallbacks | ||
4619 | 417 | def test_register_user(self): | ||
4620 | 418 | """Test the call from the client.""" | ||
4621 | 419 | app_name = 'app' | ||
4622 | 420 | email = 'email' | ||
4623 | 421 | password = 'password' | ||
4624 | 422 | displayname = 'name' | ||
4625 | 423 | captcha_id = 'captcha_id' | ||
4626 | 424 | captcha_solution = 'captcha_solution' | ||
4627 | 425 | self.login.root = self.root | ||
4628 | 426 | |||
4629 | 427 | self.root.register_user(app_name, email, password, displayname, | ||
4630 | 428 | captcha_id, captcha_solution, | ||
4631 | 429 | self.login.emit_user_registered, | ||
4632 | 430 | self.login.emit_user_registration_error) | ||
4633 | 431 | self.mocker.replay() | ||
4634 | 432 | yield self.client.register_user(app_name, email, password, displayname, | ||
4635 | 433 | captcha_id, captcha_solution) | ||
4636 | 434 | yield self.client.unregister_to_signals() | ||
4637 | 435 | self.mocker.verify() | ||
4638 | 436 | |||
4639 | 437 | @defer.inlineCallbacks | ||
4640 | 438 | def test_emit_logged_in(self): | ||
4641 | 439 | """Test that the cb was called.""" | ||
4642 | 440 | app_name = 'app' | ||
4643 | 441 | result = 'result' | ||
4644 | 442 | |||
4645 | 443 | self.login.emit_logged_in(app_name, result) | ||
4646 | 444 | yield self.assert_fired("logged_in", app_name) | ||
4647 | 445 | |||
4648 | 446 | @defer.inlineCallbacks | ||
4649 | 447 | def test_emit_login_error(self): | ||
4650 | 448 | """Test that the db was called.""" | ||
4651 | 449 | app_name = 'app' | ||
4652 | 450 | |||
4653 | 451 | self.patch(main, "get_token_name", lambda _: None) | ||
4654 | 452 | self.patch(self.login.root.processor, "login", | ||
4655 | 453 | TestException.fail) | ||
4656 | 454 | self.login.login(app_name, "email", "password") | ||
4657 | 455 | yield self.assert_fired("login_error", app_name) | ||
4658 | 456 | |||
4659 | 457 | @defer.inlineCallbacks | ||
4660 | 458 | def test_emit_user_not_validated(self): | ||
4661 | 459 | """Test that the cb was called.""" | ||
4662 | 460 | app_name = 'app' | ||
4663 | 461 | result = 'result' | ||
4664 | 462 | |||
4665 | 463 | self.login.emit_user_not_validated(app_name, result) | ||
4666 | 464 | yield self.assert_fired("user_not_validated", app_name) | ||
4667 | 465 | |||
4668 | 466 | @defer.inlineCallbacks | ||
4669 | 467 | def test_login(self): | ||
4670 | 468 | """Test the call from the client.""" | ||
4671 | 469 | app_name = 'app' | ||
4672 | 470 | email = 'email' | ||
4673 | 471 | password = 'password' | ||
4674 | 472 | self.login.root = self.root | ||
4675 | 473 | |||
4676 | 474 | self.root.login(app_name, email, password, | ||
4677 | 475 | self.login.emit_logged_in, | ||
4678 | 476 | self.login.emit_login_error, | ||
4679 | 477 | self.login.emit_user_not_validated) | ||
4680 | 478 | self.mocker.replay() | ||
4681 | 479 | yield self.client.login(app_name, email, password) | ||
4682 | 480 | yield self.client.unregister_to_signals() | ||
4683 | 481 | self.mocker.verify() | ||
4684 | 482 | |||
4685 | 483 | @defer.inlineCallbacks | ||
4686 | 484 | def test_emit_email_validated(self): | ||
4687 | 485 | """Test the cb was called.""" | ||
4688 | 486 | app_name = 'app' | ||
4689 | 487 | result = 'result' | ||
4690 | 488 | |||
4691 | 489 | self.login.emit_email_validated(app_name, result) | ||
4692 | 490 | yield self.assert_fired("email_validated", app_name) | ||
4693 | 491 | |||
4694 | 492 | @defer.inlineCallbacks | ||
4695 | 493 | def test_emit_email_validation_error(self): | ||
4696 | 494 | """Test the cb was called.""" | ||
4697 | 495 | app_name = 'app' | ||
4698 | 496 | |||
4699 | 497 | self.patch(main, "get_token_name", lambda _: None) | ||
4700 | 498 | self.patch(self.login.root.processor, "validate_email", | ||
4701 | 499 | TestException.fail) | ||
4702 | 500 | self.login.validate_email(app_name, "email", "password", "token") | ||
4703 | 501 | yield self.assert_fired("email_validation_error", app_name) | ||
4704 | 502 | |||
4705 | 503 | @defer.inlineCallbacks | ||
4706 | 504 | def test_validate_email(self): | ||
4707 | 505 | """Test the client calll.""" | ||
4708 | 506 | app_name = 'app' | ||
4709 | 507 | email = 'email' | ||
4710 | 508 | password = 'password' | ||
4711 | 509 | email_token = 'token' | ||
4712 | 510 | self.login.root = self.root | ||
4713 | 511 | |||
4714 | 512 | self.root.validate_email(app_name, email, password, email_token, | ||
4715 | 513 | self.login.emit_email_validated, | ||
4716 | 514 | self.login.emit_email_validation_error) | ||
4717 | 515 | self.mocker.replay() | ||
4718 | 516 | yield self.client.validate_email(app_name, email, password, | ||
4719 | 517 | email_token) | ||
4720 | 518 | yield self.client.unregister_to_signals() | ||
4721 | 519 | self.mocker.verify() | ||
4722 | 520 | |||
4723 | 521 | @defer.inlineCallbacks | ||
4724 | 522 | def test_emit_password_reset_token_sent(self): | ||
4725 | 523 | """Test the cb was called.""" | ||
4726 | 524 | app_name = 'app' | ||
4727 | 525 | result = 'result' | ||
4728 | 526 | |||
4729 | 527 | self.login.emit_password_reset_token_sent(app_name, result) | ||
4730 | 528 | yield self.assert_fired("password_reset_token_sent", app_name) | ||
4731 | 529 | |||
4732 | 530 | @defer.inlineCallbacks | ||
4733 | 531 | def test_emit_password_reset_error(self): | ||
4734 | 532 | """Test the cb was called.""" | ||
4735 | 533 | app_name = 'app' | ||
4736 | 534 | |||
4737 | 535 | self.patch(self.login.root.processor, "request_password_reset_token", | ||
4738 | 536 | TestException.fail) | ||
4739 | 537 | self.login.request_password_reset_token(app_name, "email") | ||
4740 | 538 | yield self.assert_fired("password_reset_error", app_name) | ||
4741 | 539 | |||
4742 | 540 | @defer.inlineCallbacks | ||
4743 | 541 | def test_request_password_reset_token(self): | ||
4744 | 542 | """Test the client call.""" | ||
4745 | 543 | app_name = 'app' | ||
4746 | 544 | email = 'email' | ||
4747 | 545 | self.login.root = self.root | ||
4748 | 546 | |||
4749 | 547 | self.root.request_password_reset_token(app_name, email, | ||
4750 | 548 | self.login.emit_password_reset_token_sent, | ||
4751 | 549 | self.login.emit_password_reset_error) | ||
4752 | 550 | self.mocker.replay() | ||
4753 | 551 | self.client.request_password_reset_token(app_name, email) | ||
4754 | 552 | yield self.client.unregister_to_signals() | ||
4755 | 553 | self.mocker.verify() | ||
4756 | 554 | |||
4757 | 555 | @defer.inlineCallbacks | ||
4758 | 556 | def test_emit_password_changed(self): | ||
4759 | 557 | """Test the cb was called.""" | ||
4760 | 558 | app_name = 'app' | ||
4761 | 559 | result = 'result' | ||
4762 | 560 | |||
4763 | 561 | self.login.emit_password_changed(app_name, result) | ||
4764 | 562 | yield self.assert_fired("password_changed", app_name) | ||
4765 | 563 | |||
4766 | 564 | @defer.inlineCallbacks | ||
4767 | 565 | def test_emit_password_change_error(self): | ||
4768 | 566 | """Test the cb was called.""" | ||
4769 | 567 | app_name = 'app' | ||
4770 | 568 | |||
4771 | 569 | self.patch(self.login.root.processor, "set_new_password", | ||
4772 | 570 | TestException.fail) | ||
4773 | 571 | self.login.set_new_password(app_name, "email", "token", "password") | ||
4774 | 572 | yield self.assert_fired("password_change_error", app_name) | ||
4775 | 573 | |||
4776 | 574 | @defer.inlineCallbacks | ||
4777 | 575 | def test_set_new_password(self): | ||
4778 | 576 | """Test the client call.""" | ||
4779 | 577 | app_name = 'app' | ||
4780 | 578 | email = 'email' | ||
4781 | 579 | token = 'token' | ||
4782 | 580 | new_password = 'password' | ||
4783 | 581 | self.login.root = self.root | ||
4784 | 582 | |||
4785 | 583 | self.root.set_new_password(app_name, email, token, new_password, | ||
4786 | 584 | self.login.emit_password_changed, | ||
4787 | 585 | self.login.emit_password_change_error) | ||
4788 | 586 | self.mocker.replay() | ||
4789 | 587 | yield self.client.set_new_password(app_name, email, token, | ||
4790 | 588 | new_password) | ||
4791 | 589 | yield self.client.unregister_to_signals() | ||
4792 | 590 | self.mocker.verify() | ||
4793 | 591 | |||
4794 | 592 | |||
4795 | 593 | class CredentialsManagementTestCase(ConnectedTestCase, TestCase): | ||
4796 | 594 | """Test the management class.""" | ||
4797 | 595 | |||
4798 | 596 | @defer.inlineCallbacks | ||
4799 | 597 | def setUp(self): | ||
4800 | 598 | """Set up tests.""" | ||
4801 | 599 | yield super(CredentialsManagementTestCase, self).setUp() | ||
4802 | 600 | self.mocker = Mocker() | ||
4803 | 601 | self.root = self.mocker.mock() | ||
4804 | 602 | self.except_to_errdict = self.mocker.replace( | ||
4805 | 603 | 'ubuntu_sso.main.except_to_errdict') | ||
4806 | 604 | self.creds = CredentialsManagement(None, None) | ||
4807 | 605 | self.creds.root = self.root | ||
4808 | 606 | # start pb | ||
4809 | 607 | self.sso_root = UbuntuSSORoot(cred_manager=self.creds) | ||
4810 | 608 | # pylint: disable=E1101 | ||
4811 | 609 | yield self.setup_client_server(self.sso_root) | ||
4812 | 610 | self.client = yield self._get_client() | ||
4813 | 611 | # pylint: enable=E1101 | ||
4814 | 612 | |||
4815 | 613 | @defer.inlineCallbacks | ||
4816 | 614 | def _get_client(self): | ||
4817 | 615 | """Get the client.""" | ||
4818 | 616 | # request the remote object and create a client | ||
4819 | 617 | root = yield self.client_factory.getRootObject() | ||
4820 | 618 | remote = yield root.callRemote('get_cred_manager') | ||
4821 | 619 | client = CredentialsManagementClient(remote) | ||
4822 | 620 | yield client.register_to_signals() | ||
4823 | 621 | self.addCleanup(client.unregister_to_signals) | ||
4824 | 622 | # set the cb | ||
4825 | 623 | for signal_name in ['on_authorization_denied_cb', | ||
4826 | 624 | 'on_credentials_found_cb', | ||
4827 | 625 | 'on_credentials_not_found_cb', | ||
4828 | 626 | 'on_credentials_cleared_cb', | ||
4829 | 627 | 'on_credentials_stored_cb', | ||
4830 | 628 | 'on_credentials_error_cb']: | ||
4831 | 629 | setattr(client, signal_name, self.mocker.mock()) | ||
4832 | 630 | defer.returnValue(client) | ||
4833 | 631 | |||
4834 | 632 | @defer.inlineCallbacks | ||
4835 | 633 | def test_shutdown(self): | ||
4836 | 634 | """Test that root is called.""" | ||
4837 | 635 | # pylint: disable=W0104 | ||
4838 | 636 | self.root.ref_count | ||
4839 | 637 | # pylint: enable=W0104 | ||
4840 | 638 | self.mocker.result(1) | ||
4841 | 639 | self.root.shutdown() | ||
4842 | 640 | self.mocker.replay() | ||
4843 | 641 | yield self.client.shutdown() | ||
4844 | 642 | yield self.client.unregister_to_signals() | ||
4845 | 643 | |||
4846 | 644 | @defer.inlineCallbacks | ||
4847 | 645 | def test_emit_authorization_denied(self): | ||
4848 | 646 | """Test the callback is called.""" | ||
4849 | 647 | app_name = 'app' | ||
4850 | 648 | |||
4851 | 649 | # pylint: disable=W0104 | ||
4852 | 650 | self.root.ref_count | ||
4853 | 651 | # pylint: enable=W0104 | ||
4854 | 652 | self.mocker.result(1) | ||
4855 | 653 | self.root.ref_count = 0 | ||
4856 | 654 | self.client.on_authorization_denied_cb(app_name) | ||
4857 | 655 | self.mocker.replay() | ||
4858 | 656 | self.creds.emit_authorization_denied(app_name) | ||
4859 | 657 | yield self.client.unregister_to_signals() | ||
4860 | 658 | self.mocker.verify() | ||
4861 | 659 | |||
4862 | 660 | @defer.inlineCallbacks | ||
4863 | 661 | def test_emit_credentials_found(self): | ||
4864 | 662 | """Test the callback is called.""" | ||
4865 | 663 | app_name = 'app' | ||
4866 | 664 | creds = 'creds' | ||
4867 | 665 | |||
4868 | 666 | # pylint: disable=W0104 | ||
4869 | 667 | self.root.ref_count | ||
4870 | 668 | # pylint: enable=W0104 | ||
4871 | 669 | self.mocker.result(1) | ||
4872 | 670 | self.root.ref_count = 0 | ||
4873 | 671 | self.client.on_credentials_found_cb(app_name, creds) | ||
4874 | 672 | self.mocker.replay() | ||
4875 | 673 | self.creds.emit_credentials_found(app_name, creds) | ||
4876 | 674 | yield self.client.unregister_to_signals() | ||
4877 | 675 | self.mocker.verify() | ||
4878 | 676 | |||
4879 | 677 | @defer.inlineCallbacks | ||
4880 | 678 | def test_emit_credentials_not_found(self): | ||
4881 | 679 | """Test the callback is called.""" | ||
4882 | 680 | app_name = 'app' | ||
4883 | 681 | |||
4884 | 682 | # pylint: disable=W0104 | ||
4885 | 683 | self.root.ref_count | ||
4886 | 684 | # pylint: enable=W0104 | ||
4887 | 685 | self.mocker.result(1) | ||
4888 | 686 | self.root.ref_count = 0 | ||
4889 | 687 | self.client.on_credentials_not_found_cb(app_name) | ||
4890 | 688 | self.mocker.replay() | ||
4891 | 689 | self.creds.emit_credentials_not_found(app_name) | ||
4892 | 690 | yield self.client.unregister_to_signals() | ||
4893 | 691 | self.mocker.verify() | ||
4894 | 692 | |||
4895 | 693 | @defer.inlineCallbacks | ||
4896 | 694 | def test_emit_credentials_cleared(self): | ||
4897 | 695 | """Test the callback is called.""" | ||
4898 | 696 | app_name = 'app' | ||
4899 | 697 | |||
4900 | 698 | # pylint: disable=W0104 | ||
4901 | 699 | self.root.ref_count | ||
4902 | 700 | # pylint: enable=W0104 | ||
4903 | 701 | self.mocker.result(1) | ||
4904 | 702 | self.root.ref_count = 0 | ||
4905 | 703 | self.client.on_credentials_cleared_cb(app_name) | ||
4906 | 704 | self.mocker.replay() | ||
4907 | 705 | self.creds.emit_credentials_cleared(app_name) | ||
4908 | 706 | yield self.client.unregister_to_signals() | ||
4909 | 707 | self.mocker.verify() | ||
4910 | 708 | |||
4911 | 709 | @defer.inlineCallbacks | ||
4912 | 710 | def test_emit_credentials_stored(self): | ||
4913 | 711 | """Test the callback is called.""" | ||
4914 | 712 | app_name = 'app' | ||
4915 | 713 | |||
4916 | 714 | # pylint: disable=W0104 | ||
4917 | 715 | self.root.ref_count | ||
4918 | 716 | # pylint: enable=W0104 | ||
4919 | 717 | self.mocker.result(1) | ||
4920 | 718 | self.root.ref_count = 0 | ||
4921 | 719 | self.client.on_credentials_stored_cb(app_name) | ||
4922 | 720 | self.mocker.replay() | ||
4923 | 721 | self.creds.emit_credentials_stored(app_name) | ||
4924 | 722 | yield self.client.unregister_to_signals() | ||
4925 | 723 | self.mocker.verify() | ||
4926 | 724 | |||
4927 | 725 | @defer.inlineCallbacks | ||
4928 | 726 | def test_emit_credentials_error(self): | ||
4929 | 727 | """Test the callback is called.""" | ||
4930 | 728 | app_name = 'app' | ||
4931 | 729 | raised_error = 'error' | ||
4932 | 730 | |||
4933 | 731 | # pylint: disable=W0104 | ||
4934 | 732 | self.root.ref_count | ||
4935 | 733 | # pylint: enable=W0104 | ||
4936 | 734 | self.mocker.result(1) | ||
4937 | 735 | self.root.ref_count = 0 | ||
4938 | 736 | self.client.on_credentials_error_cb(app_name, raised_error) | ||
4939 | 737 | self.mocker.replay() | ||
4940 | 738 | self.creds.emit_credentials_error(app_name, raised_error) | ||
4941 | 739 | yield self.client.unregister_to_signals() | ||
4942 | 740 | self.mocker.verify() | ||
4943 | 741 | |||
4944 | 742 | @defer.inlineCallbacks | ||
4945 | 743 | def test_find_credentials(self): | ||
4946 | 744 | """Test that root is called.""" | ||
4947 | 745 | app_name = 'app' | ||
4948 | 746 | args = 'args' | ||
4949 | 747 | |||
4950 | 748 | # pylint: disable=W0212 | ||
4951 | 749 | self.root.find_credentials(app_name, args, MATCH(callable), | ||
4952 | 750 | self.creds._process_failure) | ||
4953 | 751 | # pylint: enable=W0212 | ||
4954 | 752 | self.root.shutdown() | ||
4955 | 753 | yield self.client.find_credentials(app_name, args) | ||
4956 | 754 | yield self.client.unregister_to_signals() | ||
4957 | 755 | |||
4958 | 756 | @defer.inlineCallbacks | ||
4959 | 757 | def test_clear_credentials(self): | ||
4960 | 758 | """Test that root is called.""" | ||
4961 | 759 | app_name = 'app' | ||
4962 | 760 | args = 'args' | ||
4963 | 761 | |||
4964 | 762 | # pylint: disable=W0212 | ||
4965 | 763 | self.root.clear_credentials(app_name, args, MATCH(callable), | ||
4966 | 764 | self.creds._process_failure) | ||
4967 | 765 | # pylint: enable=W0212 | ||
4968 | 766 | self.mocker.replay() | ||
4969 | 767 | yield self.client.clear_credentials(app_name, args) | ||
4970 | 768 | yield self.client.unregister_to_signals() | ||
4971 | 769 | |||
4972 | 770 | @defer.inlineCallbacks | ||
4973 | 771 | def test_store_credentials(self): | ||
4974 | 772 | """Test that root is called.""" | ||
4975 | 773 | app_name = 'app' | ||
4976 | 774 | args = 'args' | ||
4977 | 775 | |||
4978 | 776 | # pylint: disable=W0212 | ||
4979 | 777 | self.root.store_credentials(app_name, args, MATCH(callable), | ||
4980 | 778 | self.creds._process_failure) | ||
4981 | 779 | # pylint: enable=W0212 | ||
4982 | 780 | self.mocker.replay() | ||
4983 | 781 | yield self.client.store_credentials(app_name, args) | ||
4984 | 782 | yield self.client.unregister_to_signals() | ||
4985 | 783 | |||
4986 | 784 | @defer.inlineCallbacks | ||
4987 | 785 | def test_register(self): | ||
4988 | 786 | """Test that root is called.""" | ||
4989 | 787 | app_name = 'app' | ||
4990 | 788 | args = 'args' | ||
4991 | 789 | |||
4992 | 790 | self.root.register(app_name, args) | ||
4993 | 791 | self.mocker.replay() | ||
4994 | 792 | yield self.client.register(app_name, args) | ||
4995 | 793 | yield self.client.unregister_to_signals() | ||
4996 | 794 | |||
4997 | 795 | @defer.inlineCallbacks | ||
4998 | 796 | def test_login(self): | ||
4999 | 797 | """Test that root is called.""" | ||
5000 | 798 | app_name = 'app' |
The diff has been truncated for viewing.
Thank you for your work there