Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 5 additions & 19 deletions membersuite_api_client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
import base64
import hmac

from .utils import get_session_id


XHTML_NAMESPACE = "http://membersuite.com/schemas"


Expand Down Expand Up @@ -39,12 +42,6 @@ def get_hashed_signature(self, url):
hashed = hmac.new(secret_b, data_b, sha1).digest()
return base64.b64encode(hashed).decode("utf-8")

def get_session_id_from_login_result(self, login_result):
try:
return login_result["header"]["header"]["SessionId"]
except TypeError:
return None

def request_session(self):
"""
Performs initial request to initialize session and get session id
Expand All @@ -57,12 +54,11 @@ def request_session(self):
result = self.client.service.WhoAmI(
_soapheaders=[concierge_request_header])

self.session_id = self.get_session_id_from_login_result(
login_result=result)
self.session_id = get_session_id(result=result)

if not self.session_id:
raise MembersuiteLoginError(
result["body"]["LoginResult"]["Errors"])
result["body"]["WhoAmIResult"]["Errors"])

return self.session_id

Expand Down Expand Up @@ -135,16 +131,6 @@ def query_orgs(self, parameters=None, since_when=None):
else:
return None

def convert_ms_object(self, ms_object):
"""
Converts the list of dictionaries with keys "key" and "value" into
more logical value-key pairs in a plain dictionary.
"""
out_dict = {}
for item in ms_object:
out_dict[item["Key"]] = item["Value"]
return out_dict

def runSQL(self, query, start_record=0):
concierge_request_header = self.construct_concierge_header(
url="http://membersuite.com/contracts/"
Expand Down
Empty file.
24 changes: 24 additions & 0 deletions membersuite_api_client/security/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from ..utils import convert_ms_object


class PortalUser(object):

def __init__(self, portal_user, session_id=None):
"""Create a PortalUser object from a the Zeep'ed XML representation of
a Membersuite PortalUser object.

"""
fields = convert_ms_object(portal_user["Fields"]
["KeyValueOfstringanyType"])

self.id = fields["ID"]
self.email_address = fields["EmailAddress"]
self.first_name = fields["FirstName"]
self.last_name = fields["LastName"]

self.session_id = session_id

self.extra_data = portal_user

def get_username(self):
return "_membersuite_id_{}".format(self.id)
40 changes: 40 additions & 0 deletions membersuite_api_client/security/services.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from .models import PortalUser
from ..utils import get_session_id


class LoginToPortalError(Exception):

def __init__(self, result):
self.result = result


def login_to_portal(username, password, client):
"""Log `username` into the MemberSuite Portal.

Returns a PortalUser object if successful, raises
LoginToPortalError if not.

"""
if not client.session_id:
client.request_session()

concierge_request_header = client.construct_concierge_header(
url=("http://membersuite.com/contracts/IConciergeAPIService/"
"LoginToPortal"))

result = client.client.service.LoginToPortal(
_soapheaders=[concierge_request_header],
portalUserName=username,
portalPassword=password)

login_to_portal_result = result["body"]["LoginToPortalResult"]

if login_to_portal_result["Success"]:
portal_user = login_to_portal_result["ResultValue"]["PortalUser"]

session_id = get_session_id(result=result)

return PortalUser(portal_user=portal_user,
session_id=session_id)
else:
raise LoginToPortalError(result=result)
3 changes: 2 additions & 1 deletion membersuite_api_client/subscriptions/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"""

from .models import Subscription
from ..utils import convert_ms_object


class SubscriptionService(object):
Expand Down Expand Up @@ -48,7 +49,7 @@ def get_org_subscriptions(self, org_id, publication_id=None):

subscription_list = []
for obj in objects:
sane_obj = self.client.convert_ms_object(
sane_obj = convert_ms_object(
obj['Fields']['KeyValueOfstringanyType'])
subscription = Subscription(
id=sane_obj['ID'],
Expand Down
33 changes: 4 additions & 29 deletions membersuite_api_client/tests/test_client.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import datetime
import os
import unittest

from ..client import ConciergeClient
import datetime
from ..utils import get_session_id


MS_USER_ID = os.environ.get("MS_USER_ID", None)
MS_USER_PASS = os.environ.get("MS_USER_PASS", None)
MS_ACCESS_KEY = os.environ["MS_ACCESS_KEY"]
MS_SECRET_KEY = os.environ["MS_SECRET_KEY"]
MS_ASSOCIATION_ID = os.environ["MS_ASSOCIATION_ID"]
Expand Down Expand Up @@ -55,9 +54,8 @@ def test_request_session(self):
# Check that the session ID in the response headers matches the
# previously obtained session, so the user was not re-authenticated
# but properly used the established session.
self.assertEqual(
client.get_session_id_from_login_result(login_result=response),
client.session_id)
self.assertEqual(get_session_id(result=response),
client.session_id)

def test_query_orgs(self):
"""
Expand All @@ -84,29 +82,6 @@ def test_query_orgs(self):
response = client.query_orgs(parameters, since_when)
# self.assertFalse(response)

def test_convert_ms_object(self):
"""
Can we parse the list of dicts for org attributes into a dict?
"""
client = ConciergeClient(access_key=MS_ACCESS_KEY,
secret_key=MS_SECRET_KEY,
association_id=MS_ASSOCIATION_ID)

# Send a login request to receive a session id
session_id = client.request_session()
self.assertTrue(session_id)
parameters = {
'Name': 'AASHE Test Campus',
}
response = client.query_orgs(parameters)
self.assertEqual(response[0]["Fields"]["KeyValueOfstringanyType"]
[28]["Value"],
'AASHE Test Campus')
converted_dict = client.convert_ms_object(
response[0]["Fields"]["KeyValueOfstringanyType"])

self.assertEqual(converted_dict["Name"], "AASHE Test Campus")


if __name__ == '__main__':
unittest.main()
31 changes: 31 additions & 0 deletions membersuite_api_client/tests/test_security.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import os
import unittest

from ..client import ConciergeClient
from ..security.models import PortalUser
from ..security.services import login_to_portal, LoginToPortalError


class SecurityServicesTestCase(unittest.TestCase):

@classmethod
def setUpClass(cls):
cls.client = ConciergeClient(
access_key=os.environ["MS_ACCESS_KEY"],
secret_key=os.environ["MS_SECRET_KEY"],
association_id=os.environ["MS_ASSOCIATION_ID"])

def test_login_to_portal(self):
"""Can we log in to the portal?"""
portal_user = login_to_portal(
username=os.environ["TEST_MS_PORTAL_USER_ID"],
password=os.environ["TEST_MS_PORTAL_USER_PASS"],
client=self.client)
self.assertIsInstance(portal_user, PortalUser)

def test_login_to_portal_failure(self):
"""What happens when we can't log in to the portal?"""
with self.assertRaises(LoginToPortalError):
login_to_portal(username="bo-o-o-gus user ID",
password="wrong password",
client=self.client)
40 changes: 40 additions & 0 deletions membersuite_api_client/tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import os
import unittest

from ..client import ConciergeClient
from ..utils import convert_ms_object


MS_ACCESS_KEY = os.environ["MS_ACCESS_KEY"]
MS_SECRET_KEY = os.environ["MS_SECRET_KEY"]
MS_ASSOCIATION_ID = os.environ["MS_ASSOCIATION_ID"]


class UtilsTestCase(unittest.TestCase):

def test_convert_ms_object(self):
"""
Can we parse the list of dicts for org attributes into a dict?
"""
client = ConciergeClient(access_key=MS_ACCESS_KEY,
secret_key=MS_SECRET_KEY,
association_id=MS_ASSOCIATION_ID)

# Send a login request to receive a session id
session_id = client.request_session()
self.assertTrue(session_id)
parameters = {
'Name': 'AASHE Test Campus',
}
response = client.query_orgs(parameters)
self.assertEqual(response[0]["Fields"]["KeyValueOfstringanyType"]
[28]["Value"],
'AASHE Test Campus')
converted_dict = convert_ms_object(
response[0]["Fields"]["KeyValueOfstringanyType"])

self.assertEqual(converted_dict["Name"], "AASHE Test Campus")


if __name__ == '__main__':
unittest.main()
20 changes: 20 additions & 0 deletions membersuite_api_client/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
def convert_ms_object(ms_object):
"""
Converts the list of dictionaries with keys "key" and "value" into
more logical value-key pairs in a plain dictionary.
"""
out_dict = {}
for item in ms_object:
out_dict[item["Key"]] = item["Value"]
return out_dict


def get_session_id(result):
"""Returns the Session ID for an API result.

When there's no Session ID, returns None.
"""
try:
return result["header"]["header"]["SessionId"]
except TypeError:
return None