-
Notifications
You must be signed in to change notification settings - Fork 0
Make centralauth work, write tests #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,7 @@ | ||
| *.pyc | ||
| __pycache__ | ||
| /.cache | ||
| /.coverage | ||
| /htmlcov | ||
| /*.egg-info | ||
| __pycache__ | ||
| .*.sw? | ||
| *.pyc |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,10 +9,13 @@ | |
| ''' # noqa: E501 | ||
| import mwapi | ||
|
|
||
| from . import about | ||
| from . import errors | ||
|
|
||
| USER_AGENT = "{} -- {}".format(about.__name__, about.__author_email__) | ||
|
|
||
| class CentralAuth: | ||
|
|
||
| class CentralAuth(object): | ||
|
|
||
| def __init__(self, ca_session): | ||
| self.ca_session = ca_session | ||
|
|
@@ -31,8 +34,7 @@ def get_globaluser_info(self, gu_id): | |
| return gui_doc | ||
|
|
||
| def get_localuser_info(self, name, context_doc): | ||
| session = mwapi.Session( | ||
| context_doc['url'], self.ca_session.user_agent) | ||
| session = mwapi.Session(context_doc['url'], user_agent = USER_AGENT) | ||
| doc = session.get( | ||
| action='query', list='users', ususers={name}, usprop={'groups'}) | ||
| lui_doc = doc['query']['users'][0] | ||
|
|
@@ -45,17 +47,18 @@ def check_user_rights(self, gu_id, context, requirements): | |
| gui_doc = self.get_globaluser_info(gu_id) | ||
|
|
||
| if 'locked' in gui_doc: | ||
| raise errors.UserLockedError( | ||
| "The user account with gu_id={0} is locked".format(gu_id)) | ||
| raise errors.UserLockedError(gu_id) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice. Much better |
||
|
|
||
| # Check for local context | ||
| if context in gui_doc['merged']: | ||
| context_doc = gui_doc['merged'][context] | ||
|
|
||
| if 'blocked' in context_doc: | ||
| raise errors.UserBlockedError( | ||
| "the user account with gu_id={0} is blocked on {1}: {2}" | ||
| .format(gu_id, context, context_doc['blocked'])) | ||
| gu_id, | ||
| context, | ||
| context_doc['blocked']['expiry'], | ||
| context_doc['blocked']['reason']) | ||
|
|
||
| lui_doc = self.get_localuser_info(gui_doc['name'], context_doc) | ||
| local_groups = lui_doc.get('groups', []) or [] | ||
|
|
@@ -66,4 +69,15 @@ def check_user_rights(self, gu_id, context, requirements): | |
|
|
||
| @classmethod | ||
| def from_config(cls, config): | ||
| return cls(config['centralauth']) | ||
| # Extract config. | ||
| session_config = config['centralauth'].copy() | ||
| if 'user_agent' not in session_config: | ||
| session_config['user_agent'] = USER_AGENT | ||
| # Pull host out as it's not a keyword arg. | ||
| host = session_config['host'] | ||
| del(session_config['host']) | ||
|
|
||
| # Create session based on config. | ||
| ca_session = mwapi.Session(host, **session_config) | ||
|
|
||
| return cls(ca_session) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,249 @@ | ||
| import mwapi | ||
| import pytest | ||
|
|
||
| from jade.centralauth import CentralAuth | ||
| from jade import errors | ||
| from . import util | ||
|
|
||
| test_config = { | ||
| 'centralauth': { | ||
| # Global info host can be arbitrary? | ||
| 'host': 'https://www.mediawiki.org', | ||
| }, | ||
| } | ||
|
|
||
| # TODO: Made this up cos I haven't found a missing user yet. | ||
| missing_gui_response = { | ||
| 'batchcomplete': '', | ||
| 'query': { | ||
| 'globaluserinfo': { | ||
| 'missing': '', | ||
| } | ||
| } | ||
| } | ||
|
|
||
| enwiki_blocked_gui_response = { | ||
| 'batchcomplete': '', | ||
| 'query': { | ||
| 'globaluserinfo': { | ||
| 'home': 'enwiki', | ||
| 'id': 19729909, | ||
| 'registration': '2013-04-14T06:48:25Z', | ||
| 'name': 'FUCKYOU', | ||
| 'groups': [], | ||
| 'rights': [], | ||
| 'merged': [ | ||
| { | ||
| 'wiki': 'enwiki', | ||
| 'url': 'https://en.wikipedia.org', | ||
| 'timestamp': '2013-04-14T06:48:25Z', | ||
| 'method': 'primary', | ||
| 'editcount': 0, | ||
| 'registration': '2006-03-26T17:01:06Z', | ||
| 'blocked': { | ||
| 'expiry': 'infinity', | ||
| 'reason': '{{username}}' | ||
| }, | ||
| }, | ||
| ] | ||
| } | ||
| } | ||
| } | ||
|
|
||
| locked_gui_response = { | ||
| 'batchcomplete': '', | ||
| 'query': { | ||
| 'globaluserinfo': { | ||
| 'home': 'metawiki', | ||
| 'id': 33085348, | ||
| 'registration': '2014-08-17T23:22:31Z', | ||
| 'name': 'Jalexander', | ||
| 'locked': '', | ||
| 'groups': [], | ||
| 'rights': [], | ||
| 'merged': [ | ||
| { | ||
| 'wiki': 'commonswiki', | ||
| 'url': 'https://commons.wikimedia.org', | ||
| 'timestamp': '2017-12-12T19:44:56Z', | ||
| 'method': 'login', | ||
| 'editcount': 0, | ||
| 'registration': '2017-12-12T19:44:56Z', | ||
| }, | ||
| { | ||
| 'wiki': 'loginwiki', | ||
| 'url': 'https://login.wikimedia.org', | ||
| 'timestamp': '2014-08-17T23:22:33Z', | ||
| 'method': 'login', | ||
| 'editcount': 0, | ||
| 'registration': '2014-08-17T23:22:33Z', | ||
| }, | ||
| { | ||
| 'wiki': 'metawiki', | ||
| 'url': 'https://meta.wikimedia.org', | ||
| 'timestamp': '2014-08-17T23:22:31Z', | ||
| 'method': 'new', | ||
| 'editcount': 0, | ||
| 'registration': '2014-08-17T23:22:31Z', | ||
| }, | ||
| { | ||
| 'wiki': 'nowikimedia', | ||
| 'url': 'https://no.wikimedia.org', | ||
| 'timestamp': '2017-12-13T09:53:07Z', | ||
| 'method': 'login', | ||
| 'editcount': 0, | ||
| 'registration': '2017-12-13T09:53:07Z', | ||
| } | ||
| ] | ||
| } | ||
| } | ||
| } | ||
|
|
||
| autopatrolled_metawiki_gui_response = { | ||
| "batchcomplete": "", | ||
| "query": { | ||
| "globaluserinfo": { | ||
| "home": "enwiki", | ||
| "id": 15622560, | ||
| "registration": "2012-12-21T22:37:22Z", | ||
| "name": "Awight (WMF)", | ||
| "groups": [ | ||
| "oathauth-tester", | ||
| "wmf-researcher" | ||
| ], | ||
| "merged": [ | ||
| { | ||
| "wiki": "metawiki", | ||
| "url": "https://meta.wikimedia.org", | ||
| "timestamp": "2012-12-21T23:59:21Z", | ||
| "method": "login", | ||
| "editcount": 222, | ||
| "registration": "2012-12-21T23:59:21Z", | ||
| "groups": [ | ||
| "autopatrolled" | ||
| ] | ||
| }, | ||
| { | ||
| "wiki": "ptwiki", | ||
| "url": "https://pt.wikipedia.org", | ||
| "timestamp": "2013-04-09T01:26:35Z", | ||
| "method": "login", | ||
| "editcount": 0, | ||
| "registration": "2013-04-09T01:26:35Z" | ||
| }, | ||
| ], | ||
| "unattached": [] | ||
| } | ||
| } | ||
| } | ||
|
|
||
| autopatrolled_metawiki_lui_response = { | ||
| 'batchcomplete': '', | ||
| 'query': { | ||
| 'users': [{ | ||
| 'userid': 2043420, | ||
| 'name': 'Awight (WMF)', | ||
| 'groups': ['autopatrolled', '*', 'user', 'autoconfirmed'] | ||
| }] | ||
| } | ||
| } | ||
|
|
||
| no_autopatrolled_metawiki_lui_response = { | ||
| 'batchcomplete': '', | ||
| 'query': { | ||
| 'users': [{ | ||
| 'userid': 2043420, | ||
| 'name': 'Awight (WMF)', | ||
| 'groups': ['*', 'user', 'autoconfirmed'] | ||
| }] | ||
| } | ||
| } | ||
|
|
||
|
|
||
| def test_from_config(): | ||
| ca = CentralAuth.from_config(test_config) | ||
| assert isinstance(ca, CentralAuth) | ||
|
|
||
|
|
||
| # TODO: We can't test this using the current mocking strategy, because the | ||
| # error comes from within .get | ||
| # def test_get_globaluser_info_invalid_user_id(): | ||
| # ca = CentralAuth.from_config(test_config) | ||
| # with pytest.raises(mwapi.errors.APIError): | ||
| # ca.get_globaluser_info(0) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure I understand this TODO.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It turns out, requesting non-existent users actually causes an APIError in mwapi, rather than the "missing" sad path. We probably need to handle this, and calling functions will be expecting differents types of jade.errors.UserPermissionError. We should consider wrapping the APIError... Also, the TODO is because I hadn't decided which code we should be checking. Now I think we should be mocking mwapi.Session.get to just throw an error, but first let's decide how the CentralAuth class will handle it. |
||
|
|
||
|
|
||
| def test_get_globaluser_info_missing_user(): | ||
| ca = CentralAuth.from_config(test_config) | ||
|
|
||
| with util.mock_mwapi_get(missing_gui_response): | ||
| with pytest.raises(errors.GlobalUserExistenceError) as exc_info: | ||
| gui_doc = ca.get_globaluser_info(12345) | ||
|
|
||
| assert exc_info.value.gu_id == 12345 | ||
|
|
||
|
|
||
| def test_get_globaluser_info(): | ||
| ca = CentralAuth.from_config(test_config) | ||
|
|
||
| with util.mock_mwapi_get(enwiki_blocked_gui_response): | ||
| gui_doc = ca.get_globaluser_info(19729909) | ||
|
|
||
| assert gui_doc['home'] == 'enwiki' | ||
| assert gui_doc['name'] == 'FUCKYOU' | ||
|
|
||
|
|
||
| def test_check_user_rights_blocked_in_context(): | ||
| ca = CentralAuth.from_config(test_config) | ||
| with util.mock_mwapi_get(enwiki_blocked_gui_response): | ||
| with pytest.raises(errors.UserBlockedError) as exc_info: | ||
| ca.check_user_rights(19729909, 'enwiki', ['autopatrolled']) | ||
|
|
||
| assert exc_info.value.gu_id == 19729909 | ||
| assert exc_info.value.context == 'enwiki' | ||
| assert exc_info.value.expiry == 'infinity' | ||
| assert exc_info.value.reason == '{{username}}' | ||
|
|
||
|
|
||
| def test_check_user_rights_blocked_other_context(): | ||
| ca = CentralAuth.from_config(test_config) | ||
| with util.mock_mwapi_get(enwiki_blocked_gui_response): | ||
| ca.check_user_rights(19729909, 'frwiki', ['autopatrolled']) | ||
|
|
||
| assert True | ||
|
|
||
|
|
||
| def test_check_user_rights_locked(): | ||
| ca = CentralAuth.from_config(test_config) | ||
| #with mock.patch('mwapi.session.get') as mock_mwapi: | ||
| # mock_mwapi.side_effect = [ | ||
| # enwiki_blocked_gui_response, | ||
| # enwiki_blocked_lui_response, | ||
| # ] | ||
| with util.mock_mwapi_get(locked_gui_response): | ||
| with pytest.raises(errors.UserLockedError) as exc_info: | ||
| ca.check_user_rights(33085348, 'enwiki', ['autopatrolled']) | ||
|
|
||
| assert exc_info.value.gu_id == 33085348 | ||
|
|
||
|
|
||
| def test_check_user_rights_has_autopatrolled(): | ||
| ca = CentralAuth.from_config(test_config) | ||
| with util.mock_mwapi_get([autopatrolled_metawiki_gui_response, | ||
| autopatrolled_metawiki_lui_response]): | ||
| ca.check_user_rights(15622560, 'metawiki', ['autopatrolled']) | ||
|
|
||
| assert True | ||
|
|
||
|
|
||
| def test_check_user_rights_lacks_autopatrolled(): | ||
| ca = CentralAuth.from_config(test_config) | ||
| with util.mock_mwapi_get([autopatrolled_metawiki_gui_response, | ||
| no_autopatrolled_metawiki_lui_response]): | ||
| with pytest.raises(errors.UserRightsError) as exc_info: | ||
| ca.check_user_rights(15622560, 'metawiki', ['autopatrolled']) | ||
|
|
||
| assert exc_info.value.gu_id == 15622560 | ||
| assert exc_info.value.context == 'metawiki' | ||
| assert exc_info.value.required == ['autopatrolled'] | ||
| assert exc_info.value.user_groups == ['*', 'user', 'autoconfirmed'] | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No longer necessary as of Python 3.0. https://stackoverflow.com/questions/15374857/should-all-python-classes-extend-object