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

== Python Lint Notices ==
ubuntu_ sso/main/ linux.py:
29: [W0611] Unused import sys
ubuntu_ sso/main/ tests/test_ windows. py:
21: [W0611] Unused import defer
23: [W0611] Unused import UbuntuSSOService