Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksey Lim <alsroot@sugarlabs.org>2012-01-14 19:40:10 (GMT)
committer Aleksey Lim <alsroot@sugarlabs.org>2012-01-14 19:40:10 (GMT)
commitf7397765c3d98b52ae3f0123ebd7b970ba2bb1bd (patch)
tree8973ad0db0b5a4320beb3700ec05149b4224c23c
parent745f38cfd5bbe82de191e49b9ff1a66651f96542 (diff)
Create common User document; support registration
-rw-r--r--restful_document/__init__.py1
-rw-r--r--restful_document/document.py10
-rw-r--r--restful_document/router.py13
-rw-r--r--restful_document/user.py85
-rwxr-xr-xtests/units/user.py46
5 files changed, 148 insertions, 7 deletions
diff --git a/restful_document/__init__.py b/restful_document/__init__.py
index 9c09d54..22f427e 100644
--- a/restful_document/__init__.py
+++ b/restful_document/__init__.py
@@ -16,3 +16,4 @@
from restful_document.document import Document, restful_method
from restful_document.metadata import Metadata, Method
from restful_document.router import Router
+from restful_document.user import User
diff --git a/restful_document/document.py b/restful_document/document.py
index 792f80e..2f9adca 100644
--- a/restful_document/document.py
+++ b/restful_document/document.py
@@ -27,13 +27,13 @@ class Document(ad.Document):
@classmethod
@restful_method(method='POST')
- def _restful_create(cls):
+ def restful_post(cls):
doc = cls.create(env.request.content)
return {'guid': doc.guid}
@classmethod
@restful_method(method='GET')
- def _restful_find(cls, **kwargs):
+ def restful_get(cls, **kwargs):
offset = env.pop_int('offset', kwargs, None)
limit = env.pop_int('limit', kwargs, None)
query = env.pop_str('query', kwargs, None)
@@ -46,7 +46,7 @@ class Document(ad.Document):
'documents': [i.all_properties(reply) for i in documents]}
@restful_method(method='PUT')
- def _restful_update(self, prop=None):
+ def restful_put(self, prop=None):
if prop is None:
self.update(self.guid, env.request.content)
elif isinstance(self.metadata[prop], ad.BlobProperty):
@@ -57,13 +57,13 @@ class Document(ad.Document):
self.post()
@restful_method(method='DELETE')
- def _restful_delete(self, prop=None):
+ def restful_delete(self, prop=None):
enforce(prop is None, env.Forbidden,
_('Properties cannot be deleted'))
self.delete(self.guid)
@restful_method(method='GET')
- def _restful_get(self, prop=None):
+ def restful_get_document(self, prop=None):
if prop is None:
reply = []
for name, prop in self.metadata.items():
diff --git a/restful_document/router.py b/restful_document/router.py
index f1cd360..43e2806 100644
--- a/restful_document/router.py
+++ b/restful_document/router.py
@@ -15,6 +15,7 @@
import json
import types
+import logging
from gettext import gettext as _
import active_document as ad
@@ -24,6 +25,9 @@ from restful_document.metadata import Metadata
from restful_document.util import enforce
+_logger = logging.getLogger('rd.router')
+
+
class Router(object):
def __init__(self, classes):
@@ -34,6 +38,9 @@ class Router(object):
env.request.set(environ)
env.responce.set()
+ _logger.debug('Processing request %s: %s',
+ env.request.url, env.request.content)
+
try:
method = self.metadata.get_method()
enforce(method is not None and \
@@ -42,16 +49,18 @@ class Router(object):
result = method()
except Exception, error:
- util.exception(_('Error while processing %s request'),
- env.request.url)
if isinstance(error, ad.Unauthorized):
+ util.exception()
env.responce.status = env.Unauthorized.status
env.responce.update(env.Unauthorized.headers)
elif isinstance(error, env.HTTPError):
env.responce.status = error.status
env.responce.update(error.headers)
else:
+ util.exception(_('Error while processing "%s" request'),
+ env.request.url)
env.responce.status = '500 Internal Server Error'
+
env.responce['Content-Type'] = 'application/json'
result = {'error': str(error), 'request': env.request.url}
diff --git a/restful_document/user.py b/restful_document/user.py
new file mode 100644
index 0000000..3a7f770
--- /dev/null
+++ b/restful_document/user.py
@@ -0,0 +1,85 @@
+# Copyright (C) 2012, Aleksey Lim
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import hashlib
+from gettext import gettext as _
+
+from M2Crypto import DSA
+
+import active_document as ad
+
+from restful_document import env, util
+from restful_document.metadata import restful_method
+from restful_document.document import Document
+from restful_document.util import enforce
+
+
+class User(Document):
+
+ def __init__(self, guid=None, manual_guid=None, *args, **kwargs):
+ Document.__init__(self, guid, *args, **kwargs)
+ if guid is None and manual_guid is not None:
+ self._guid = manual_guid
+
+ @ad.active_property(slot=1, prefix='N', full_text=True)
+ def nickname(self, value):
+ return value
+
+ @ad.active_property(ad.StoredProperty)
+ def color(self, value):
+ return value
+
+ @ad.active_property(slot=2, prefix='S', permissions=ad.ACCESS_CREATE)
+ def machine_sn(self, value):
+ return value
+
+ @ad.active_property(slot=3, prefix='U', permissions=ad.ACCESS_CREATE)
+ def machine_uuid(self, value):
+ return value
+
+ @ad.active_property(ad.StoredProperty, permissions=ad.ACCESS_CREATE)
+ def pubkey(self, value):
+ return value
+
+ @classmethod
+ @restful_method(method='POST')
+ def restful_post(cls):
+ props = env.request.content
+ enforce('pubkey' in props,
+ _('Property "pubkey" is required for user registeration'))
+ props['manual_guid'], props['pubkey'] = \
+ _load_pubkey(props['pubkey'].strip())
+ doc = cls.create(env.request.content)
+ return {'guid': doc.guid}
+
+
+def _load_pubkey(pubkey):
+ if 'SSH_ASKPASS' in os.environ:
+ # Otherwise ssh-keygen will popup auth dialog
+ del os.environ['SSH_ASKPASS']
+
+ try:
+ src_path = util.TempFilePath(text=pubkey)
+ dst_pubkey = util.assert_call(
+ ['ssh-keygen', '-f', src_path, '-e', '-m', 'PKCS8'])
+ dst_path = util.TempFilePath(text=dst_pubkey)
+ DSA.load_pub_key(dst_path)
+ except Exception:
+ message = _('Cannot read DSS public key gotten for registeration')
+ util.exception(message)
+ raise env.Forbidden(message)
+
+ return str(hashlib.sha1(pubkey.split()[1]).hexdigest()), dst_pubkey
diff --git a/tests/units/user.py b/tests/units/user.py
new file mode 100755
index 0000000..a237611
--- /dev/null
+++ b/tests/units/user.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+# sugar-lint: disable
+
+import json
+
+import restkit
+
+from __init__ import tests
+from tests import Resource
+
+import restful_document as rd
+
+
+VALID_DSS_PUBKEY = """\
+ssh-dss AAAAB3NzaC1kc3MAAACBAOl6rwXgjVrsGAry6rFHljYE94pQAVynFJPFbzvP9zy5oLg58WvMTr9LvYzS1kB0Y3ym6yQl0RRS8CUtbGpuDaD0mIXjbkfukE7CkvQ3v+lIybk30QgvkxO7nQa4+NW5QwvASU6E1ODFa2QTW15/Fq3MhMYH+3UqZv5g/bBrikoXAAAAFQDctWzPfX1I4hL2wYfR2U2M4bnXaQAAAIEAl5fWWWX2OiYz9i4aza4PGUFUAc3RI+ksrmGrL0FZ5hTiTx/iqc2uhGiQY5CqVyb+/E76flQVkSvlghRmGh4lwrbFPj4PIMP23M5lqU/Dk2srnB1WC+huK/wLHw11NJrRIAsufcycIowe9w09GMsfCzVXcETttvKB+l35KoF1irYAAACBAL0vRSHl+bnTHW97FUkh+ycDTKM/eZSi0zI8Ptcw7DU81T/d5fU7aJtUqx7Inxot0YKeZx2ZeWAVzE9opoK8cnvawDkomRhL1m8IUiC0uCNAEG2C1a3uioY+OJbvq/J1QP6KIwidlzu8m536o8odgTow29j5exw9+i3cbr+KiT4R
+"""
+INVALID_DSS_PUBKEY = """\
+ssh-dss _AAAB3NzaC1kc3MAAACBAOl6rwXgjVrsGAry6rFHljYE94pQAVynFJPFbzvP9zy5oLg58WvMTr9LvYzS1kB0Y3ym6yQl0RRS8CUtbGpuDaD0mIXjbkfukE7CkvQ3v+lIybk30QgvkxO7nQa4+NW5QwvASU6E1ODFa2QTW15/Fq3MhMYH+3UqZv5g/bBrikoXAAAAFQDctWzPfX1I4hL2wYfR2U2M4bnXaQAAAIEAl5fWWWX2OiYz9i4aza4PGUFUAc3RI+ksrmGrL0FZ5hTiTx/iqc2uhGiQY5CqVyb+/E76flQVkSvlghRmGh4lwrbFPj4PIMP23M5lqU/Dk2srnB1WC+huK/wLHw11NJrRIAsufcycIowe9w09GMsfCzVXcETttvKB+l35KoF1irYAAACBAL0vRSHl+bnTHW97FUkh+ycDTKM/eZSi0zI8Ptcw7DU81T/d5fU7aJtUqx7Inxot0YKeZx2ZeWAVzE9opoK8cnvawDkomRhL1m8IUiC0uCNAEG2C1a3uioY+OJbvq/J1QP6KIwidlzu8m536o8odgTow29j5exw9+i3cbr+KiT4R
+"""
+UID_FOR_PUBKEY = 'd26cef70447160f31a7497cc0320f23a4e383cc3'
+
+
+class UserTest(tests.Test):
+
+ def test_Register(self):
+ self.httpd(8000, [rd.User])
+ rest = Resource('http://localhost:8000')
+
+ props = {
+ 'nickname': 'foo',
+ 'color': '',
+ 'machine_sn': 'machine_sn',
+ 'machine_uuid': 'machine_uuid',
+ }
+
+ props['pubkey'] = INVALID_DSS_PUBKEY
+ self.assertRaises(restkit.Unauthorized, rest.post, '/user', payload=json.dumps(props))
+
+ props['pubkey'] = VALID_DSS_PUBKEY
+ reply = json.loads(
+ rest.post('/user', payload=json.dumps(props)).body_string())
+ self.assertEqual(UID_FOR_PUBKEY, reply['guid'])
+
+
+if __name__ == '__main__':
+ tests.main()