<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>base/oauth.py</filename>
    </added>
    <added>
      <filename>controllers/admin.py</filename>
    </added>
    <added>
      <filename>tally.py</filename>
    </added>
    <added>
      <filename>templates/admin/clients.tmpl</filename>
    </added>
    <added>
      <filename>templates/admin/index.tmpl</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -43,11 +43,7 @@ class DBObject(db.Model):
         
     @classmethod
     def selectByKey(cls, key_name, key_value):
-        obj = cls()
-        if obj.select(keys={key_name:key_value}):
-            return obj
-        else:
-            return None
+      return cls.selectByKeys({key_name: key_value})
     
     @classmethod
     def selectByKeys(cls, keys):</diff>
      <filename>base/DBObjectGAE.py</filename>
    </modified>
    <modified>
      <diff>@@ -4,14 +4,12 @@ The stuff for the base package
 
 import cherrypy
 
-from base import utils
-
 try:
   from django.utils import simplejson
 except:
   import simplejson
   
-import session
+import session, oauth, utils
 import sys, traceback
 
 FAILURE = &quot;failure&quot;</diff>
      <filename>base/__init__.py</filename>
    </modified>
    <modified>
      <diff>@@ -14,7 +14,7 @@ try:
 except:
   pass
 
-import models as do
+from base import oauth
 
 def get_session():
     if not hasattr(cherrypy.serving,'base_session') or cherrypy.serving.base_session == None:
@@ -27,83 +27,98 @@ def get_status():
 
 def set_status(status):
     get_session().set_status(status)
-    
+
+def get_api_client():
+  &quot;&quot;&quot;
+  Determine if this is an API client making the call
+  &quot;&quot;&quot;
+  # verify the OAuth request
+  request = cherrypy.request
+
+  oauth_request = oauth.OAuthRequest.from_request(request.method, request.path_info, headers= request.headers,
+                                                  parameters=request.params, query_string=None)
+  try:
+    consumer, token, params = Session.OAUTH_SERVER.verify_request(oauth_request)
+    return consumer
+  except oauth.OAuthError:
+    return None
+  
+
 def login_protect(func, redirect_to = None):
     &quot;&quot;&quot;
-    A decorator that enables cherrypy
-    but also that enables certain global property checking
+    A decorator that enables checks that the request is authenticated
     &quot;&quot;&quot;
-    def ensure_user_logged_in(self, *args, **kwargs):
-        if not get_session().get_user():
-            raise cherrypy.HTTPRedirect(redirect_to or users.create_login_url(&quot;/&quot;))
+    def ensure_auth(self, *args, **kwargs):
+      if not get_session().get_user() or get_api_client():
+        raise cherrypy.HTTPRedirect(redirect_to or users.create_login_url(&quot;/&quot;))
         
-        return func(self, *args, **kwargs)
+      return func(self, *args, **kwargs)
     
-    return ensure_user_logged_in
-
-
-def login_protect_class(cls, redirect_to = None):
-    def ensure_user_logged_in(self):
-        if not get_session().get_user():
-            raise cherrypy.HTTPRedirect(redirect_to or users.create_login_url(&quot;/&quot;))
-        
-    cls.before_filter = ensure_user_logged_in
+    return ensure_auth
+
+def admin_protect(func, redirect_to = None):
+  &quot;&quot;&quot;
+  A decorator that enables checks that the request is authenticated
+  &quot;&quot;&quot;
+  def ensure_admin(self, *args, **kwargs):
+    if not get_session().is_admin():
+      raise cherrypy.HTTPRedirect(redirect_to or users.create_login_url(&quot;/&quot;))
+      
+    return func(self, *args, **kwargs)
+  
+  return ensure_admin
 
 def logout():
     get_session().logout()
 
 class Session:
-    def __init__(self, cherrypy_session=None):
-        self._user = users.get_current_user()
-        #if self.has_key('user_id'):
-        #    self._user = do.User.selectById(self[&quot;user_id&quot;])
-
-    def __getitem__(self, key):
-        if self._cp_session.has_key(key):
-            return self._cp_session[key]
-        else:
-            return None
-
-    def __setitem__(self, key, value):
-        self._cp_session[key] = value
-
-    def __delitem__(self, key):
-        del self._cp_session[key]
-
-    def has_key(self, key):
-        return self._cp_session.has_key(key)
-
-    def login(self, email, password):
-        user = do.User.select_by_email(email)
-        if not user:
-            raise Exception('no such user')
-
-        if not user.verified_p:
-            self.set_status('Your account has not been verified: &lt;a href=&quot;/user/send_confirmation_email?email=%s&quot;&gt;send confirmation email&lt;/a&gt;.' % email)
-            return None
-
-        if user.verify_password(password):
-            self['user_id'] = user.user_id
-            self._user = user
-            return self._user
-        else:
-            self.set_status('bad password')
-            return None
-
-    def logout(self):
-        self._user = None
-        #del self['user_id']
-        
-    def get_user(self):
-        return self._user
+  ## OAUTH
+
+  OAUTH_SERVER = None
+
+  @classmethod
+  def setup_oauth(cls, oauth_datastore):
+    Session.OAUTH_SERVER = oauth.OAuthServer(oauth_datastore)
+    Session.OAUTH_SERVER.add_signature_method(oauth.OAuthSignatureMethod_HMAC_SHA1())
 
-    def set_user(self, user):
-        self._user = user
+  def __init__(self, cherrypy_session=None):
+    self._user = users.get_current_user()
+    self._admin_p = users.is_current_user_admin()
+    #if self.has_key('user_id'):
+    #    self._user = do.User.selectById(self[&quot;user_id&quot;])
 
-    def set_status(self, status):
-        pass
-        #self['__status'] = status
+  def __getitem__(self, key):
+    if self._cp_session.has_key(key):
+      return self._cp_session[key]
+    else:
+      return None
 
-    def get_status(self):
-        return &quot;&quot;
+  def __setitem__(self, key, value):
+    self._cp_session[key] = value
+
+  def __delitem__(self, key):
+    del self._cp_session[key]
+
+  def has_key(self, key):
+    return self._cp_session.has_key(key)
+
+  def logout(self):
+    self._user = None
+    #del self['user_id']
+        
+  def get_user(self):
+    return self._user
+        
+  def set_user(self, user):
+    self._user = user
+
+  def set_status(self, status):
+    pass
+    #self['__status'] = status
+
+  def get_status(self):
+    return &quot;&quot;
+        
+  def is_admin(self):
+    return self._admin_p
 </diff>
      <filename>base/session.py</filename>
    </modified>
    <modified>
      <diff>@@ -1 +1 @@
-import basic, user, election
\ No newline at end of file
+import basic, user, election, admin
\ No newline at end of file</diff>
      <filename>controllers/__init__.py</filename>
    </modified>
    <modified>
      <diff>@@ -50,19 +50,22 @@ class VoterController(REST.Resource):
     &quot;&quot;&quot;
     View a single voter's info as JSON.
     &quot;&quot;&quot;
-    v_dict = voter.toJSONDict()
-    v_dict['vote'] = voter.get_vote()
-    return v_dict
+    return voter.toJSONDict(with_vote=True)
     
   @web
   @json
-  def list(self, **kw):
+  def list(self, with_vote=False, category=None, after=None, limit=None, **kw):
     &quot;&quot;&quot;
     Output a JSON list of all voters for a given election.
     &quot;&quot;&quot;
     election = self.parent
-    voters = election.get_voters()
-    return [v.toJSONDict() for v in voters]
+
+    # normalize limit
+    limit = int(limit or 500)
+    if limit &gt; 500: limit = 500
+      
+    voters = election.get_voters(category= category, after=after, limit= limit)
+    return [v.toJSONDict(with_vote=with_vote) for v in voters]
 
   @web
   @session.login_protect
@@ -171,6 +174,13 @@ class ElectionController(REST.Resource):
   ########
   
   @web
+  def test(self, **kw):
+    &quot;&quot;&quot;
+    Testing OAuth
+    &quot;&quot;&quot;
+    return session.get_api_client().key
+    
+  @web
   def verifier(self):
     &quot;&quot;&quot;
     The JavaScript election verifier code.</diff>
      <filename>controllers/election.py</filename>
    </modified>
    <modified>
      <diff>@@ -10,7 +10,7 @@ indexes:
 # automatically uploaded to the admin console when you next deploy
 # your application using appcfg.py.
 
-# Used once in query history.
+# Used 3 times in query history.
 - kind: ElectionExponent
   properties:
   - name: election
@@ -36,3 +36,9 @@ indexes:
   - name: election
   - name: tallied_at
   - name: cast_id
+
+# Used 20 times in query history.
+- kind: Voter
+  properties:
+  - name: election
+  - name: voter_id</diff>
      <filename>index.yaml</filename>
    </modified>
    <modified>
      <diff>@@ -9,4 +9,30 @@ try:
   from google.appengine.ext import db
   from modelsGAE import *
 except:
-  from modelsStandalone import *
\ No newline at end of file
+  from modelsStandalone import *
+  
+from base import oauth
+# example store for one of each thing
+class OAuthDataStore(oauth.OAuthDataStore):
+  def __init__(self):
+    pass
+      
+  def lookup_consumer(self, key):
+    logging.info(&quot;looking up consumer %s&quot; % key)
+    c = APIClient.selectByKey('consumer_key', key)
+    return oauth.OAuthConsumer(c.consumer_key, c.consumer_secret)
+
+  def lookup_token(self, oauth_consumer, token_type, token):
+    logging.info(&quot;looking up token %s&quot; % token)
+    if token_type != 'access':
+      raise NotImplementedError
+
+    c = APIClient.selectByKey('consumer_key', oauth_consumer.key)
+    return oauth.OAuthToken(c.access_token, c.access_token_secret)
+
+  def lookup_nonce(self, oauth_consumer, oauth_token, nonce):
+    &quot;&quot;&quot;
+    Fix this to actually check for nonces
+    &quot;&quot;&quot;
+    return None
+</diff>
      <filename>models/__init__.py</filename>
    </modified>
    <modified>
      <diff>@@ -24,9 +24,22 @@ from google.appengine.ext import db
 from google.appengine.api import users
 from google.appengine.ext import webapp
 
-
+##
+## Machine API
+##
+
+class APIClient(mbase.APIClient):
+  consumer_key = db.StringProperty()
+  consumer_secret = db.StringProperty()
+  access_token = db.StringProperty()
+  access_token_secret = db.StringProperty()
+  
 class Election(mbase.ElectionBase):
   admin = db.UserProperty()
+  
+  # if machine-able API
+  api_client = db.ReferenceProperty(APIClient)
+  
   name = db.StringProperty(multiline=False)
   public_key_json = db.TextProperty()
   private_key_json = db.TextProperty()
@@ -108,4 +121,3 @@ class Voter(mbase.VoterBase):
     super(Voter, self).save()
       
 
-</diff>
      <filename>models/modelsGAE.py</filename>
    </modified>
    <modified>
      <diff>@@ -4,7 +4,7 @@ Base stuff for all models
 
 #from base import utils
 from base.DBObject import DBObject
-from base import utils
+from base import utils, oauth, session
 
 try:
   from django.utils import simplejson
@@ -71,7 +71,7 @@ class ElectionBase(DBObject):
     if category:
       keys['category'] = category
     
-    return models.Voter.selectAllByKeys(keys, after=after, limit=limit)
+    return models.Voter.selectAllByKeys(keys, order_by= 'voter_id', after=after, limit=limit)
     
   def get_cast_votes(self, offset=None, limit=None):
     return [voter.get_vote() for voter in self.get_voters(offset = offset, limit = limit) if voter.cast_id != None]
@@ -353,6 +353,12 @@ class VoterBase(DBObject):
   def get_vote(self):
     return utils.from_json(self.vote or &quot;null&quot;)
     
+  def toJSONDict(self, with_vote = False):
+    json_dict = super(VoterBase, self).toJSONDict()
+    if with_vote:
+      json_dict['vote'] = self.get_vote()
+    return json_dict
+    
   def verifyProofsAndTally(self, election, running_tally):
     # copy the tally array
     new_running_tally = [[a for a in q] for q in running_tally]
@@ -426,3 +432,12 @@ class VoterBase(DBObject):
     self.save()
     
     return new_running_tally
+
+
+##
+## Machine API
+##
+
+class APIClient(DBObject):
+  pass
+  </diff>
      <filename>models/modelsbase.py</filename>
    </modified>
    <modified>
      <diff>@@ -7,14 +7,20 @@ Ben Adida (ben@adida.net)
 from base import session
 session.LOGIN_URL = '/user/'
 
+# setup oauth
+import models
+session.Session.setup_oauth(models.OAuthDataStore())
+
 import cherrypy
 from controllers import *
 
+
 # mount points for various controllers
 root_controller = basic.HeliosController()
 root_controller.about = basic.AboutController()
 root_controller.user = user.UserController()
 root_controller.elections = election.ElectionController()
+root_controller.admin = admin.AdminController()
 
 root = cherrypy.tree.mount(root_controller, '/')
 </diff>
      <filename>sitemap.py</filename>
    </modified>
  </modified>
  <removed type="array">
    <removed>
      <filename>templates/sidebar.tmpl</filename>
    </removed>
    <removed>
      <filename>templates/widebar.tmpl</filename>
    </removed>
  </removed>
  <parents type="array">
    <parent>
      <id>3f1128f80176c9d9f67fc810468a0e6ec30d0016</id>
    </parent>
  </parents>
  <author>
    <name>Ben Adida</name>
    <email>ben@adida.net</email>
  </author>
  <url>http://github.com/benadida/helios/commit/a45edcd2d272561243170cd09460f7004e2b6fb1</url>
  <id>a45edcd2d272561243170cd09460f7004e2b6fb1</id>
  <committed-date>2008-08-30T12:36:44-07:00</committed-date>
  <authored-date>2008-08-30T12:36:44-07:00</authored-date>
  <message>added oauth api</message>
  <tree>3ef1bb2a6768872c796b9e272eb50908957fc81e</tree>
  <committer>
    <name>Ben Adida</name>
    <email>ben@adida.net</email>
  </committer>
</commit>
