Compare commits

...

38 Commits

Author SHA1 Message Date
Ben Lopatin 44cf317aa9 Fix version cleaning 2018-10-29 18:15:41 -04:00
Ben Lopatin 468f6b58cd Version bump 0.8.0
Closes gh-14
2018-10-29 18:11:35 -04:00
Ben Lopatin c0db7215c9 Format with black 2018-10-29 18:02:58 -04:00
Ben Lopatin b0b277c917 Add PR template 2018-10-29 18:02:19 -04:00
Ben Lopatin 7793f3ae54 Merge pull request #12 from goetzk/patch-1
Increase minimum requests version
2018-02-14 19:25:47 -05:00
Karl Goetz c900fad726 Increase minimum requests version
With the addition of json support in PR #9, the required version of requests has increased to that which introduced json={} syntax.
2018-02-15 11:03:19 +11:00
Ben Lopatin 6c4c40d93c Merge pull request #10 from goetzk/new-api-methods
New api methods
2017-12-13 20:27:58 -05:00
Karl Goetz ff49cc7219 Merge branch 'master' into new-api-methods 2017-11-30 22:25:00 +11:00
Karl Goetz 3e391c38ec Merge branch 'master' into new-api-methods 2017-11-30 22:22:36 +11:00
Karl Goetz 010bfa624c Add docstrings to methods
Requested in #10
2017-11-09 09:25:38 +11:00
Karl Goetz 006b7d416a Revert change to site_settings
Adding a settings arg changed the interface, something I was trying to avoid on
this branch.
Picked up by @bennylope when reviewing #10.
2017-11-09 08:40:16 +11:00
Ben Lopatin 7b3733ca8e Merge pull request #9 from goetzk/json-support
Json support
2017-11-08 12:35:31 -05:00
Alvaro Molina Alvarez a87503eec3 adding support over topics 2017-10-21 16:08:26 +11:00
Alvaro Molina Alvarez f0dd191b58 getting user emails 2017-10-21 16:07:52 +11:00
Alvaro Molina Alvarez 6cffef4e49 Change group_members to handle paging 2017-10-21 16:06:13 +11:00
Alvaro Molina Alvarez 12356819ea adding tags support 2017-10-21 16:04:55 +11:00
Alvaro Molina Alvarez 84016afbc5 adding new client's methods 2017-10-21 16:04:23 +11:00
Alvaro Molina Alvarez e5fe47d0a6 updating client methods 2017-10-21 16:01:59 +11:00
Alvaro Molina Alvarez 2fde21b51f adding trust level lock method 2017-10-21 16:01:38 +11:00
Alvaro Molina Alvarez 2d2e8d1695 adding upload image client method 2017-10-21 16:00:42 +11:00
Alvaro Molina Alvarez 217b606ee7 adding avatar support when a group is added 2017-10-21 15:58:47 +11:00
Alvaro Molina Alvarez fd815ac97b adding color schemes method 2017-10-21 15:58:02 +11:00
Alvaro Molina Alvarez 9fbfa39060 adding new functions to the client 2017-10-21 15:56:44 +11:00
Alvaro Molina Alvarez 3ab8689b6e Add JSON support to more methods
This includes proper support for JSON being added to _put, originally in [1]

[1] a1ac18e852
2017-10-21 15:52:56 +11:00
Alvaro Molina Alvarez 914e22cc55 adding update_avatar client method 2017-10-21 15:47:39 +11:00
Karl Goetz f42a457514 New add_group_members API call
Taken from [1] by Alvaro Molina Alvarez, split to keep merges on a single
topic.
[1] 74414d1429
2017-10-21 15:45:03 +11:00
Karl Goetz 8faa1cfaf9 Group kwargs for Discourse API v1.7
Taken from [1] by Alvaro Molina Alvarez
[1] 74414d1429
2017-10-21 15:42:41 +11:00
Karl Goetz 227f7a3205 Add JSON support to client library
Newer versions of Discourse API use JSON PUTs and POSTs extensively.

This is a modified cherry pick of [1] by Alvaro Molina Alvarez
[1] 74414d1429
2017-10-21 15:40:29 +11:00
Ben Lopatin 17faed6fa7 Update CONTRIBUTING.rst 2017-10-06 09:41:18 -04:00
Ben Lopatin b761d28494 Update CONTRIBUTING.rst 2017-10-06 09:39:36 -04:00
Ben Lopatin 9108939503 Merge pull request #7 from citadelgrad/patch-1
Update client.py
2017-01-17 10:17:31 -05:00
Scott Nixon 8555abf680 Update client.py
Updated create_category method documentation.
2017-01-15 14:03:23 -08:00
Ben Lopatin dd9b7fad19 Merge pull request #6 from Polytechnique-org/master
Adding some API calls, notably on groups and users
2016-10-17 15:53:04 -04:00
Pierre-Alain Dupont a18203c8cb Adds the possibility of deleting a group 2016-10-16 15:38:09 +02:00
wilhelmhb 630b822a9a add method for getting all the available informations about a user 2016-10-15 19:34:02 +02:00
wilhelmhb f0fd17c3a3 fonctions to get data on group members/owners 2016-10-15 17:58:30 +02:00
wilhelmhb f7f1aafc64 add forgotten argument 2016-10-15 16:06:18 +02:00
wilhelmhb e77074c5d4 add possibility to create a group 2016-10-15 15:49:22 +02:00
12 changed files with 754 additions and 233 deletions
+8
View File
@@ -0,0 +1,8 @@
### Summary of changes
## Checklist
- [ ] Changes represent a *discrete update*
- [ ] Commit messages are meaningful and descriptive
- [ ] Changeset does not include any extraneous changes unrelated to the discrete change
+3
View File
@@ -7,3 +7,6 @@ Daniel Zohar
Matheus Fernandes
Scott Nixon
Jason Dorweiler
Pierre-Alain Dupont
Karl Goetz
+15
View File
@@ -12,6 +12,21 @@ Please use `Google docstring format
This *will* be enforced.
Pull requests
=============
Reviewing and merging pull requests is work, so whatever you can do to make this
easier for the package maintainer not only speed up the process of getting your
changes merged but also ensure they are. These few guidelines help significantly.
If they are confusing or you need help understanding how to accomplish them,
please ask for help in an issue.
- Please do make sure your chnageset represents a *discrete update*. If you would like to fix formatting, by all means, but don't mix that up with a bug fix. Those are separate PRs.
- Please do make sure that both your pull request description and your commits are meaningful and descriptive. Rebase first, if need be.
- Please do make sure your changeset does not include more commits than necessary. Rebase first, if need be.
- Please do make sure the changeset is not very big. If you have a large change propose it in an issue first.
- Please do make sure your changeset is based on a branch from the current HEAD of the fork you wish to merge against. This is a general best practice. Rebase first, if need be.
Testing
=======
+30
View File
@@ -3,6 +3,36 @@
Release history
===============
0.8.0
-----
- Add some PR guidance
- Add support for files in the core request methods
- Adds numerous new API controls, including:
- tag_group
- user_actions
- upload_image
- block
- trust_level_lock
- create_site_customization (theme)
- create_color_scheme
- color_schemes
- add_group_members
- group_members
- group_owners
- delete_group
- create_group
- group
- customize_site_texts
- delete_category
- user_emails
- update_topic_status
- create_post
- update_topic
- update_avatar
- user_all
0.7.0
-----
+2 -2
View File
@@ -51,9 +51,9 @@ copyright = u'2014, Marc Sibson'
# built documents.
#
# The short X.Y version.
version = '0.7'
version = '0.8'
# The full version, including alpha/beta/rc tags.
release = '0.7.0'
release = '0.8.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
+1 -1
View File
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
__version__ = '0.7.0'
__version__ = "0.8.0"
from pydiscourse.client import DiscourseClient
+545 -116
View File
File diff suppressed because it is too large Load Diff
+20 -16
View File
@@ -12,30 +12,32 @@ from pydiscourse.client import DiscourseClient, DiscourseError
class DiscourseCmd(cmd.Cmd):
prompt = 'discourse>'
prompt = "discourse>"
output = sys.stdout
def __init__(self, client):
cmd.Cmd.__init__(self)
self.client = client
self.prompt = '%s>' % self.client.host
self.prompt = "%s>" % self.client.host
def __getattr__(self, attr):
if attr.startswith('do_'):
if attr.startswith("do_"):
method = getattr(self.client, attr[3:])
def wrapper(arg):
args = arg.split()
kwargs = dict(a.split('=') for a in args if '=' in a)
args = [a for a in args if '=' not in a]
kwargs = dict(a.split("=") for a in args if "=" in a)
args = [a for a in args if "=" not in a]
try:
return method(*args, **kwargs)
except DiscourseError as e:
print(e, e.response.text)
return e.response
return wrapper
elif attr.startswith('help_'):
elif attr.startswith("help_"):
method = getattr(self.client, attr[5:])
def wrapper():
@@ -47,24 +49,26 @@ class DiscourseCmd(cmd.Cmd):
def postcmd(self, result, line):
try:
json.dump(result, self.output, sort_keys=True, indent=4, separators=(',', ': '))
json.dump(
result, self.output, sort_keys=True, indent=4, separators=(",", ": ")
)
except TypeError:
self.output.write(result.text)
def main():
op = optparse.OptionParser()
op.add_option('--host', default='http://localhost:4000')
op.add_option('--api-user', default='system')
op.add_option('-v', '--verbose', action='store_true')
op.add_option("--host", default="http://localhost:4000")
op.add_option("--api-user", default="system")
op.add_option("-v", "--verbose", action="store_true")
options, args = op.parse_args()
if not options.host.startswith('http'):
op.error('host must include protocol, eg http://')
if not options.host.startswith("http"):
op.error("host must include protocol, eg http://")
api_key = os.environ.get('DISCOURSE_API_KEY')
api_key = os.environ.get("DISCOURSE_API_KEY")
if not api_key:
op.error('please set DISCOURSE_API_KEY')
op.error("please set DISCOURSE_API_KEY")
client = DiscourseClient(options.host, options.api_user, api_key)
@@ -74,12 +78,12 @@ def main():
c = DiscourseCmd(client)
if args:
line = ' '.join(args)
line = " ".join(args)
result = c.onecmd(line)
c.postcmd(result, line)
else:
c.cmdloop()
if __name__ == '__main__':
if __name__ == "__main__":
main()
+23 -19
View File
@@ -45,34 +45,36 @@ def sso_validate(payload, signature, secret):
return value: The nonce used by discourse to validate the redirect URL
"""
if None in [payload, signature]:
raise DiscourseError('No SSO payload or signature.')
raise DiscourseError("No SSO payload or signature.")
if not secret:
raise DiscourseError('Invalid secret..')
raise DiscourseError("Invalid secret..")
payload = unquote(payload)
if not payload:
raise DiscourseError('Invalid payload..')
raise DiscourseError("Invalid payload..")
decoded = b64decode(payload.encode('utf-8')).decode('utf-8')
if 'nonce' not in decoded:
raise DiscourseError('Invalid payload..')
decoded = b64decode(payload.encode("utf-8")).decode("utf-8")
if "nonce" not in decoded:
raise DiscourseError("Invalid payload..")
h = hmac.new(secret.encode('utf-8'), payload.encode('utf-8'), digestmod=hashlib.sha256)
h = hmac.new(
secret.encode("utf-8"), payload.encode("utf-8"), digestmod=hashlib.sha256
)
this_signature = h.hexdigest()
if this_signature != signature:
raise DiscourseError('Payload does not match signature.')
raise DiscourseError("Payload does not match signature.")
# Discourse returns querystring encoded value. We only need `nonce`
qs = parse_qs(decoded)
return qs['nonce'][0]
return qs["nonce"][0]
def sso_payload(secret, **kwargs):
return_payload = b64encode(urlencode(kwargs).encode('utf-8'))
h = hmac.new(secret.encode('utf-8'), return_payload, digestmod=hashlib.sha256)
query_string = urlencode({'sso': return_payload, 'sig': h.hexdigest()})
return_payload = b64encode(urlencode(kwargs).encode("utf-8"))
h = hmac.new(secret.encode("utf-8"), return_payload, digestmod=hashlib.sha256)
query_string = urlencode({"sso": return_payload, "sig": h.hexdigest()})
return query_string
@@ -86,11 +88,13 @@ def sso_redirect_url(nonce, secret, email, external_id, username, **kwargs):
return value: URL to redirect users back to discourse, now logged in as user_username
"""
kwargs.update({
'nonce': nonce,
'email': email,
'external_id': external_id,
'username': username
})
kwargs.update(
{
"nonce": nonce,
"email": email,
"external_id": external_id,
"username": username,
}
)
return '/session/sso_login?%s' % sso_payload(secret, **kwargs)
return "/session/sso_login?%s" % sso_payload(secret, **kwargs)
+2 -2
View File
@@ -8,7 +8,7 @@ with open("pydiscourse/__init__.py", "r") as module_file:
for line in module_file:
if line.startswith("__version__"):
version_string = line.split("=")[1]
VERSION = version_string.strip().replace("'", "")
VERSION = version_string.strip().replace("\"", "")
setup(
@@ -22,7 +22,7 @@ setup(
url="https://github.com/bennylope/pydiscourse",
packages=find_packages(exclude=["tests.*", "tests"]),
install_requires=[
'requests>=2.0.0',
'requests>=2.4.2',
],
tests_require=[
'mock',
+70 -53
View File
@@ -5,18 +5,25 @@ import mock
from pydiscourse import client
import sys
if sys.version_info < (3,):
def b(x):
return x
else:
import codecs
def b(x):
return codecs.latin_1_encode(x)[0]
def prepare_response(request):
# we need to mocked response to look a little more real
request.return_value = mock.MagicMock(headers={'content-type': 'application/json; charset=utf-8'})
request.return_value = mock.MagicMock(
headers={"content-type": "application/json; charset=utf-8"}
)
class ClientBaseTestCase(unittest.TestCase):
@@ -25,9 +32,9 @@ class ClientBaseTestCase(unittest.TestCase):
"""
def setUp(self):
self.host = 'http://testhost'
self.api_username = 'testuser'
self.api_key = 'testkey'
self.host = "http://testhost"
self.api_username = "testuser"
self.api_key = "testkey"
self.client = client.DiscourseClient(self.host, self.api_username, self.api_key)
@@ -39,28 +46,27 @@ class ClientBaseTestCase(unittest.TestCase):
self.assertEqual(args[0], verb)
self.assertEqual(args[1], self.host + url)
kwargs = kwargs['params']
self.assertEqual(kwargs.pop('api_username'), self.api_username)
self.assertEqual(kwargs.pop('api_key'), self.api_key)
kwargs = kwargs["params"]
self.assertEqual(kwargs.pop("api_username"), self.api_username)
self.assertEqual(kwargs.pop("api_key"), self.api_key)
if verb == 'GET':
if verb == "GET":
self.assertEqual(kwargs, params)
class TestClientRequests(ClientBaseTestCase):
"""
Tests for common request handling
"""
@mock.patch('pydiscourse.client.requests')
@mock.patch("pydiscourse.client.requests")
def test_empty_content_http_ok(self, mocked_requests):
"""Empty content should not raise error
Critical to test against *bytestrings* rather than unicode
"""
mocked_response = mock.MagicMock()
mocked_response.content = b(' ')
mocked_response.content = b(" ")
mocked_response.status_code = 200
mocked_response.headers = {"content-type": "text/plain; charset=utf-8"}
@@ -69,126 +75,137 @@ class TestClientRequests(ClientBaseTestCase):
mocked_requests.request = mock.MagicMock()
mocked_requests.request.return_value = mocked_response
resp = self.client._request('GET', '/users/admin/1/unsuspend', {})
resp = self.client._request("GET", "/users/admin/1/unsuspend", {})
self.assertIsNone(resp)
@mock.patch('requests.request')
@mock.patch("requests.request")
class TestUser(ClientBaseTestCase):
def test_user(self, request):
prepare_response(request)
self.client.user('someuser')
self.assertRequestCalled(request, 'GET', '/users/someuser.json')
self.client.user("someuser")
self.assertRequestCalled(request, "GET", "/users/someuser.json")
def test_create_user(self, request):
prepare_response(request)
self.client.create_user('Test User', 'testuser', 'test@example.com', 'notapassword')
self.client.create_user(
"Test User", "testuser", "test@example.com", "notapassword"
)
self.assertEqual(request.call_count, 2)
# XXX incomplete
# XXX incomplete
def test_update_email(self, request):
prepare_response(request)
email = 'test@example.com'
self.client.update_email('someuser', email)
self.assertRequestCalled(request, 'PUT', '/users/someuser/preferences/email', email=email)
email = "test@example.com"
self.client.update_email("someuser", email)
self.assertRequestCalled(
request, "PUT", "/users/someuser/preferences/email", email=email
)
def test_update_user(self, request):
prepare_response(request)
self.client.update_user('someuser', a='a', b='b')
self.assertRequestCalled(request, 'PUT', '/users/someuser', a='a', b='b')
self.client.update_user("someuser", a="a", b="b")
self.assertRequestCalled(request, "PUT", "/users/someuser", a="a", b="b")
def test_update_username(self, request):
prepare_response(request)
self.client.update_username('someuser', 'newname')
self.assertRequestCalled(request, 'PUT',
'/users/someuser/preferences/username',
username='newname')
self.client.update_username("someuser", "newname")
self.assertRequestCalled(
request, "PUT", "/users/someuser/preferences/username", username="newname"
)
def test_by_external_id(self, request):
prepare_response(request)
self.client.by_external_id(123)
self.assertRequestCalled(request, 'GET',
'/users/by-external/123')
self.assertRequestCalled(request, "GET", "/users/by-external/123")
def test_suspend_user(self, request):
prepare_response(request)
self.client.suspend(123, 1, "Testing")
self.assertRequestCalled(request, 'PUT', '/admin/users/123/suspend',
duration=1, reason="Testing")
self.assertRequestCalled(
request, "PUT", "/admin/users/123/suspend", duration=1, reason="Testing"
)
def test_unsuspend_user(self, request):
prepare_response(request)
self.client.unsuspend(123)
self.assertRequestCalled(request, 'PUT', '/admin/users/123/unsuspend')
self.assertRequestCalled(request, "PUT", "/admin/users/123/unsuspend")
def test_user_bagdes(self, request):
prepare_response(request)
self.client.user_badges('username')
self.assertRequestCalled(request, 'GET', '/user-badges/{}.json'.format('username'))
self.client.user_badges("username")
self.assertRequestCalled(
request, "GET", "/user-badges/{}.json".format("username")
)
@mock.patch('requests.request')
@mock.patch("requests.request")
class TestTopics(ClientBaseTestCase):
def test_hot_topics(self, request):
prepare_response(request)
self.client.hot_topics()
self.assertRequestCalled(request, 'GET', '/hot.json')
self.assertRequestCalled(request, "GET", "/hot.json")
def test_latest_topics(self, request):
prepare_response(request)
self.client.latest_topics()
self.assertRequestCalled(request, 'GET', '/latest.json')
self.assertRequestCalled(request, "GET", "/latest.json")
def test_new_topics(self, request):
prepare_response(request)
self.client.new_topics()
self.assertRequestCalled(request, 'GET', '/new.json')
self.assertRequestCalled(request, "GET", "/new.json")
def test_topic(self, request):
prepare_response(request)
self.client.topic('some-test-slug', 22)
self.assertRequestCalled(request, 'GET', '/t/some-test-slug/22.json')
self.client.topic("some-test-slug", 22)
self.assertRequestCalled(request, "GET", "/t/some-test-slug/22.json")
def test_topics_by(self, request):
prepare_response(request)
r = self.client.topics_by('someuser')
self.assertRequestCalled(request, 'GET', '/topics/created-by/someuser.json')
self.assertEqual(r, request().json()['topic_list']['topics'])
r = self.client.topics_by("someuser")
self.assertRequestCalled(request, "GET", "/topics/created-by/someuser.json")
self.assertEqual(r, request().json()["topic_list"]["topics"])
def invite_user_to_topic(self, request):
prepare_response(request)
email = 'test@example.com'
email = "test@example.com"
self.client.invite_user_to_topic(email, 22)
self.assertRequestCalled(request, 'POST', '/t/22/invite.json', email=email, topic_id=22)
self.assertRequestCalled(
request, "POST", "/t/22/invite.json", email=email, topic_id=22
)
@mock.patch('requests.request')
@mock.patch("requests.request")
class MiscellaneousTests(ClientBaseTestCase):
def test_search(self, request):
prepare_response(request)
self.client.search('needle')
self.assertRequestCalled(request, 'GET', '/search.json', term='needle')
self.client.search("needle")
self.assertRequestCalled(request, "GET", "/search.json", term="needle")
def test_categories(self, request):
prepare_response(request)
r = self.client.categories()
self.assertRequestCalled(request, 'GET', '/categories.json')
self.assertEqual(r, request().json()['category_list']['categories'])
self.assertRequestCalled(request, "GET", "/categories.json")
self.assertEqual(r, request().json()["category_list"]["categories"])
def test_users(self, request):
prepare_response(request)
self.client.users()
self.assertRequestCalled(request, 'GET', '/admin/users/list/active.json')
self.assertRequestCalled(request, "GET", "/admin/users/list/active.json")
def test_badges(self, request):
prepare_response(request)
self.client.badges()
self.assertRequestCalled(request, 'GET', '/admin/badges.json')
self.assertRequestCalled(request, "GET", "/admin/badges.json")
def test_grant_badge_to(self, request):
prepare_response(request)
self.client.grant_badge_to('username', 1)
self.assertRequestCalled(request, 'POST', '/user_badges', username='username', badge_id=1)
self.client.grant_badge_to("username", 1)
self.assertRequestCalled(
request, "POST", "/user_badges", username="username", badge_id=1
)
+35 -24
View File
@@ -19,56 +19,67 @@ from pydiscourse.exceptions import DiscourseError
class SSOTestCase(unittest.TestCase):
def setUp(self):
# values from https://meta.discourse.org/t/official-single-sign-on-for-discourse/13045
self.secret = 'd836444a9e4084d5b224a60c208dce14'
self.nonce = 'cb68251eefb5211e58c00ff1395f0c0b'
self.payload = 'bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI%3D%0A'
self.signature = '2828aa29899722b35a2f191d34ef9b3ce695e0e6eeec47deb46d588d70c7cb56'
self.secret = "d836444a9e4084d5b224a60c208dce14"
self.nonce = "cb68251eefb5211e58c00ff1395f0c0b"
self.payload = "bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI%3D%0A"
self.signature = "2828aa29899722b35a2f191d34ef9b3ce695e0e6eeec47deb46d588d70c7cb56"
self.name = u'sam'
self.username = u'samsam'
self.external_id = u'hello123'
self.email = u'test@test.com'
self.redirect_url = u'/session/sso_login?sso=bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGImbmFtZT1z%0AYW0mdXNlcm5hbWU9c2Ftc2FtJmVtYWlsPXRlc3QlNDB0ZXN0LmNvbSZleHRl%0Acm5hbF9pZD1oZWxsbzEyMw%3D%3D%0A&sig=1c884222282f3feacd76802a9dd94e8bc8deba5d619b292bed75d63eb3152c0b'
self.name = u"sam"
self.username = u"samsam"
self.external_id = u"hello123"
self.email = u"test@test.com"
self.redirect_url = u"/session/sso_login?sso=bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGImbmFtZT1z%0AYW0mdXNlcm5hbWU9c2Ftc2FtJmVtYWlsPXRlc3QlNDB0ZXN0LmNvbSZleHRl%0Acm5hbF9pZD1oZWxsbzEyMw%3D%3D%0A&sig=1c884222282f3feacd76802a9dd94e8bc8deba5d619b292bed75d63eb3152c0b"
def test_missing_args(self):
with self.assertRaises(DiscourseError):
sso.sso_validate(None, self.signature, self.secret)
with self.assertRaises(DiscourseError):
sso.sso_validate('', self.signature, self.secret)
sso.sso_validate("", self.signature, self.secret)
with self.assertRaises(DiscourseError):
sso.sso_validate(self.payload, None, self.secret)
def test_invalid_signature(self):
with self.assertRaises(DiscourseError):
sso.sso_validate(self.payload, 'notavalidsignature', self.secret)
sso.sso_validate(self.payload, "notavalidsignature", self.secret)
def test_valid_nonce(self):
nonce = sso.sso_validate(self.payload, self.signature, self.secret)
self.assertEqual(nonce, self.nonce)
def test_valid_redirect_url(self):
url = sso.sso_redirect_url(self.nonce, self.secret, self.email, self.external_id, self.username, name='sam')
url = sso.sso_redirect_url(
self.nonce,
self.secret,
self.email,
self.external_id,
self.username,
name="sam",
)
self.assertIn('/session/sso_login', url[:20])
self.assertIn("/session/sso_login", url[:20])
# check its valid, using our own handy validator
params = parse_qs(urlparse(url).query)
payload = params['sso'][0]
sso.sso_validate(payload, params['sig'][0], self.secret)
payload = params["sso"][0]
sso.sso_validate(payload, params["sig"][0], self.secret)
# check the params have all the data we expect
payload = b64decode(payload.encode('utf-8')).decode('utf-8')
payload = b64decode(payload.encode("utf-8")).decode("utf-8")
payload = unquote(payload)
payload = dict((p.split('=') for p in payload.split('&')))
payload = dict((p.split("=") for p in payload.split("&")))
self.assertEqual(payload, {
'username': self.username,
'nonce': self.nonce,
'external_id': self.external_id,
'name': self.name,
'email': self.email
})
self.assertEqual(
payload,
{
"username": self.username,
"nonce": self.nonce,
"external_id": self.external_id,
"name": self.name,
"email": self.email,
},
)