summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--milkman/__init__.py11
-rw-r--r--milkman/auth.py170
-rw-r--r--milkman/tests/test_auth.py16
3 files changed, 197 insertions, 0 deletions
diff --git a/milkman/__init__.py b/milkman/__init__.py
new file mode 100644
index 0000000..727ff90
--- /dev/null
+++ b/milkman/__init__.py
@@ -0,0 +1,11 @@
1# vim: set filencoding=utf8
2"""
3Milkman - A Remember The Milk API
4
5@author: Mike Crute (mcrute@ag.com)
6@organization: American Greetings Interactive
7@date: February 03, 2010
8"""
9
10
11API_URL = "http://api.rememberthemilk.com/services"
diff --git a/milkman/auth.py b/milkman/auth.py
new file mode 100644
index 0000000..0dfb36d
--- /dev/null
+++ b/milkman/auth.py
@@ -0,0 +1,170 @@
1# vim: set filencoding=utf8
2"""
3Remember The Milk Authentication
4
5@author: Mike Crute (mcrute@ag.com)
6@organization: American Greetings Interactive
7@date: February 03, 2010
8"""
9
10import json
11import urllib
12from logging import getLogger
13from hashlib import md5
14from milkman import API_URL
15
16
17logger = getLogger('rtmapi')
18
19
20class APIPerms(object):
21
22 READ = "read"
23 WRITE = "write"
24 DELETE = "delete"
25
26
27def flatten_sorted_dict(in_dict):
28 items = sorted(in_dict.items())
29 flat_items = [''.join(item) for item in items]
30 return ''.join(flat_items)
31
32
33class APIError(Exception):
34 pass
35
36
37class Request(object):
38
39 def __init__(self, api_key, secret, service='rest'):
40 self.service = service
41 self.secret = secret
42 self.params = {
43 'format': 'json',
44 'api_key': api_key,
45 }
46
47 @classmethod
48 def get_factory(cls, api_key, secret):
49 return lambda: cls(api_key, secret)
50
51 @property
52 def url(self):
53 self._sign_request()
54 params = urllib.urlencode(self.params)
55 return '{0}/{1}?{2}'.format(API_URL, self.service, params)
56
57 def _sign_request(self):
58 param_string = self.secret
59 param_string += flatten_sorted_dict(self.params)
60 self.params['api_sig'] = md5(param_string).hexdigest()
61
62 def __setitem__(self, key, value):
63 self.params[key] = value
64
65 def __getitem__(self, key):
66 return self.params[key]
67
68 def _parse_json_data(self, data):
69 body = data['rsp']
70 status = body['stat']
71
72 if status == 'fail':
73 error = APIError(body['err']['msg'])
74 error.code = int(body['err']['code'])
75 raise error
76
77 del body['stat']
78
79 return body
80
81 def execute(self):
82 data = urllib.urlopen(self.url)
83 raw_data = data.read()
84 json_data = json.loads(raw_data)
85
86 return self._parse_json_data(json_data)
87
88
89class Authenticator(object):
90
91 def __init__(self, request_factory):
92 self.request_factory = request_factory
93 self.token = None
94
95 def authenticate(self, token=None):
96 if token:
97 self.token = token
98
99 if not self.check_token(self.token):
100 raw_input("Please visit: {0}".format(self.get_auth_url()))
101 self.token = self.get_token()
102
103 def get_auth_url(self):
104 req = self.request_factory()
105 req.service = 'auth'
106 req['perms'] = APIPerms.DELETE
107 req['frob'] = self._get_frob()
108
109 return req.url
110
111 def _get_frob(self):
112 req = self.request_factory()
113 req['method'] = 'rtm.auth.getFrob'
114 self.frob = req.execute()['frob']
115
116 return self.frob
117
118 def get_token(self):
119 req = self.request_factory()
120 req['method'] = 'rtm.auth.getToken'
121 req['frob'] = self.frob
122
123 print req.execute()
124
125 def check_token(self, token):
126 if not token:
127 return False
128
129 req = self.request_factory()
130 req['method'] = 'rtm.auth.checkToken'
131 req['token'] = token
132
133 try:
134 req.execute()
135 return True
136 except APIError, err:
137 if err.code == 98:
138 return False
139 raise
140
141
142class APIConnection(object):
143
144 def __init__(self, api_key, secret):
145 self.request_factory = Request.get_factory(api_key, secret)
146 self.auth = Authenticator(self.request_factory)
147
148 def authenticate(self, token=None):
149 self.auth.authenticate(token)
150 print self.auth.token
151
152 def get_lists(self):
153 req = self.request_factory()
154 req['method'] = 'rtm.lists.getList'
155 req['auth_token'] = self.auth.token
156
157 print req.execute()
158
159
160
161if __name__ == '__main__':
162 API_KEY = "38816a01a7937588081d33ea74b6f5ee"
163 SECRET = "d4caed275cf9a63e"
164
165 TOKEN = "54f3378cb114e6cc330e1c2fd34a11e96e2a26c5"
166
167 api = APIConnection(API_KEY, SECRET)
168 api.authenticate(TOKEN)
169 api.get_lists()
170
diff --git a/milkman/tests/test_auth.py b/milkman/tests/test_auth.py
new file mode 100644
index 0000000..8a4e7d2
--- /dev/null
+++ b/milkman/tests/test_auth.py
@@ -0,0 +1,16 @@
1# vim: set filencoding=utf8
2"""
3Authentication Test Suite
4
5@author: Mike Crute (mcrute@ag.com)
6@organization: American Greetings Interactive
7@date: February 03, 2010
8"""
9
10from milkman.auth import flatten_sorted_dict, md5_sign
11
12
13def test_sort_dict():
14 input = { 'alpha': '1', 'ahla': '2', 'beta': '3' }
15 output = flatten_sorted_dict(input)
16 assert output == 'ahla2alpha1beta3'