- accept message_filter and return_fields
[plcapi.git] / PLC / Auth.py
index 72e9d91..bb62c09 100644 (file)
@@ -4,7 +4,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: Auth.py,v 1.6 2006/10/31 23:07:43 mlhuang Exp $
+# $Id: Auth.py,v 1.10 2007/01/04 16:01:28 mlhuang Exp $
 #
 
 import crypt
 #
 
 import crypt
@@ -17,12 +17,13 @@ from PLC.Parameter import Parameter, Mixed
 from PLC.Persons import Persons
 from PLC.Nodes import Node, Nodes
 from PLC.Sessions import Session, Sessions
 from PLC.Persons import Persons
 from PLC.Nodes import Node, Nodes
 from PLC.Sessions import Session, Sessions
+from PLC.Peers import Peer, Peers
 
 class Auth(Parameter):
     """
     Base class for all API authentication methods, as well as a class
 
 class Auth(Parameter):
     """
     Base class for all API authentication methods, as well as a class
-    that can be used to represent Mixed(SessionAuth(), PasswordAuth()),
-    i.e. the two principal API authentication methods.
+    that can be used to represent Mixed(SessionAuth(), PasswordAuth(),
+    GPGAuth()), i.e. the three principal API authentication methods.
     """
 
     def __init__(self, auth = {}):
     """
 
     def __init__(self, auth = {}):
@@ -30,16 +31,68 @@ class Auth(Parameter):
 
     def check(self, method, auth, *args):
         method.type_check("auth", auth,
 
     def check(self, method, auth, *args):
         method.type_check("auth", auth,
-                          Mixed(SessionAuth(), PasswordAuth()),
+                          Mixed(SessionAuth(), PasswordAuth(), GPGAuth()),
                           (auth,) + args)
 
                           (auth,) + args)
 
-class SessionAuth(Auth):
+class GPGAuth(Auth):
     """
     Proposed PlanetLab federation authentication structure.
     """
 
     def __init__(self):
         Auth.__init__(self, {
     """
     Proposed PlanetLab federation authentication structure.
     """
 
     def __init__(self):
         Auth.__init__(self, {
+            'AuthMethod': Parameter(str, "Authentication method to use, always 'gpg'", optional = False),
+            'name': Parameter(str, "Peer or user name", optional = False),
+            'signature': Parameter(str, "Message signature", optional = False)
+            })
+
+    def check(self, method, auth, *args):
+        try:
+            peers = Peers(method.api, [auth['name']])
+            if peers:
+                if 'peer' not in method.roles:
+                    raise PLCAuthenticationFailure, "Not allowed to call method"
+
+                method.caller = peer = peers[0]
+                keys = [peer['key']]
+            else:
+                persons = Persons(method.api, {'email': auth['name'], 'enabled': True, 'peer_id': None})
+                if not persons:
+                    raise PLCAuthenticationFailure, "No such user '%s'" % auth['name']
+
+                if not set(person['roles']).intersection(method.roles):
+                    raise PLCAuthenticationFailure, "Not allowed to call method"
+
+                method.caller = person = persons[0]
+                keys = Keys(method.api, {'key_id': person['key_ids'], 'key_type': "gpg", 'peer_id': None})
+
+            if not keys:
+                raise PLCAuthenticationFailure, "No GPG key on record for peer or user '%s'"
+
+            for key in keys:
+                try:
+                    from PLC.GPG import gpg_verify
+                    gpg_verify(method.name, args, auth['signature'], key)
+                    return
+                except PLCAuthenticationFailure, fault:
+                    pass
+
+            raise fault
+
+        except PLCAuthenticationFailure, fault:
+            # XXX Send e-mail
+            raise fault
+
+class SessionAuth(Auth):
+    """
+    Secondary authentication method. After authenticating with a
+    primary authentication method, call GetSession() to generate a
+    session key that may be used for subsequent calls.
+    """
+
+    def __init__(self):
+        Auth.__init__(self, {
+            'AuthMethod': Parameter(str, "Authentication method to use, always 'session'", optional = False),
             'session': Parameter(str, "Session key", optional = False)
             })
 
             'session': Parameter(str, "Session key", optional = False)
             })
 
@@ -49,14 +102,14 @@ class SessionAuth(Auth):
         assert auth.has_key('session')
 
         # Get session record
         assert auth.has_key('session')
 
         # Get session record
-        sessions = Sessions(method.api, [auth['session']], expires = None).values()
+        sessions = Sessions(method.api, [auth['session']], expires = None)
         if not sessions:
             raise PLCAuthenticationFailure, "No such session"
         session = sessions[0]
 
         try:
             if session['node_id'] is not None:
         if not sessions:
             raise PLCAuthenticationFailure, "No such session"
         session = sessions[0]
 
         try:
             if session['node_id'] is not None:
-                nodes = Nodes(method.api, [session['node_id']]).values()
+                nodes = Nodes(method.api, {'node_id': session['node_id'], 'peer_id': None})
                 if not nodes:
                     raise PLCAuthenticationFailure, "No such node"
                 node = nodes[0]
                 if not nodes:
                     raise PLCAuthenticationFailure, "No such node"
                 node = nodes[0]
@@ -67,7 +120,7 @@ class SessionAuth(Auth):
                 method.caller = node
 
             elif session['person_id'] is not None and session['expires'] > time.time():
                 method.caller = node
 
             elif session['person_id'] is not None and session['expires'] > time.time():
-                persons = Persons(method.api, {'person_id': session['person_id'], 'enabled': True}).values()
+                persons = Persons(method.api, {'person_id': session['person_id'], 'enabled': True, 'peer_id': None})
                 if not persons:
                     raise PLCAuthenticationFailure, "No such account"
                 person = persons[0]
                 if not persons:
                     raise PLCAuthenticationFailure, "No such account"
                 person = persons[0]
@@ -129,7 +182,7 @@ class BootAuth(Auth):
         assert auth.has_key('node_id')
 
         try:
         assert auth.has_key('node_id')
 
         try:
-            nodes = Nodes(method.api, [auth['node_id']]).values()
+            nodes = Nodes(method.api, {'node_id': auth['node_id'], 'peer_id': None})
             if not nodes:
                 raise PLCAuthenticationFailure, "No such node"
             node = nodes[0]
             if not nodes:
                 raise PLCAuthenticationFailure, "No such node"
             node = nodes[0]
@@ -149,7 +202,7 @@ class BootAuth(Auth):
 
                 nodenetwork = None
                 if node['nodenetwork_ids']:
 
                 nodenetwork = None
                 if node['nodenetwork_ids']:
-                    nodenetworks = NodeNetworks(method.api, node['nodenetwork_ids']).values()
+                    nodenetworks = NodeNetworks(method.api, node['nodenetwork_ids'])
                     for nodenetwork in nodenetworks:
                         if nodenetwork['is_primary']:
                             break
                     for nodenetwork in nodenetworks:
                         if nodenetwork['is_primary']:
                             break
@@ -216,11 +269,11 @@ class PasswordAuth(Auth):
         assert auth.has_key('Username')
 
         # Get record (must be enabled)
         assert auth.has_key('Username')
 
         # Get record (must be enabled)
-        persons = Persons(method.api, {'email': auth['Username'], 'enabled': True})
+        persons = Persons(method.api, {'email': auth['Username'], 'enabled': True, 'peer_id': None})
         if len(persons) != 1:
             raise PLCAuthenticationFailure, "No such account"
 
         if len(persons) != 1:
             raise PLCAuthenticationFailure, "No such account"
 
-        person = persons.values()[0]
+        person = persons[0]
 
         if auth['Username'] == method.api.config.PLC_API_MAINTENANCE_USER:
             # "Capability" authentication, whatever the hell that was
 
         if auth['Username'] == method.api.config.PLC_API_MAINTENANCE_USER:
             # "Capability" authentication, whatever the hell that was