- fix Auth so that it parses AuthMethod and doles out the actual
[plcapi.git] / PLC / Auth.py
index be54b12..f71b634 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.8 2006/11/09 19:43:55 mlhuang Exp $
+# $Id: Auth.py,v 1.13 2007/01/30 23:09:55 mlhuang Exp $
 #
 
 import crypt
 #
 
 import crypt
@@ -18,22 +18,41 @@ from PLC.Persons import Persons
 from PLC.Nodes import Node, Nodes
 from PLC.Sessions import Session, Sessions
 from PLC.Peers import Peer, Peers
 from PLC.Nodes import Node, Nodes
 from PLC.Sessions import Session, Sessions
 from PLC.Peers import Peer, Peers
-from PLC.GPG import gpg_verify
+from PLC.Boot import notify_owners
 
 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(),
-    GPGAuth()), i.e. the three principal API authentication methods.
+    that can be used to represent all supported API authentication
+    methods.
     """
 
     """
 
-    def __init__(self, auth = {}):
+    def __init__(self, auth = None):
+        if auth is None:
+            auth = {'AuthMethod': Parameter(str, "Authentication method to use", optional = False)}
         Parameter.__init__(self, auth, "API authentication structure")
 
     def check(self, method, auth, *args):
         Parameter.__init__(self, auth, "API authentication structure")
 
     def check(self, method, auth, *args):
-        method.type_check("auth", auth,
-                          Mixed(SessionAuth(), PasswordAuth(), GPGAuth()),
-                          (auth,) + args)
+        # Method.type_check() should have checked that all of the
+        # mandatory fields were present.
+        assert 'AuthMethod' in auth
+
+        if auth['AuthMethod'] == "session":
+            expected = SessionAuth()
+        elif auth['AuthMethod'] == "password" or \
+             auth['AuthMethod'] == "capability":
+            expected = PasswordAuth()
+        elif auth['AuthMethod'] == "gpg":
+            expected = GPGAuth()
+        elif auth['AuthMethod'] == "hmac":
+            expected = BootAuth()
+        elif auth['AuthMethod'] == "anonymous":
+            expected = AnonymousAuth()
+        else:
+            raise PLCInvalidArgument("must be 'session', 'password', 'gpg', 'hmac', or 'anonymous'", "AuthMethod")
+
+        # Re-check using the specified authentication method
+        method.type_check("auth", auth, expected, (auth,) + args)
 
 class GPGAuth(Auth):
     """
 
 class GPGAuth(Auth):
     """
@@ -57,21 +76,22 @@ class GPGAuth(Auth):
                 method.caller = peer = peers[0]
                 keys = [peer['key']]
             else:
                 method.caller = peer = peers[0]
                 keys = [peer['key']]
             else:
-                persons = Persons(method.api, {'email': auth['name'], 'enabled': True})
+                persons = Persons(method.api, {'email': auth['name'], 'enabled': True, 'peer_id': None})
                 if not persons:
                 if not persons:
-                    raise PLCAuthenticationFailure, "No such peer or user '%s'" % auth['name']
+                    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]
 
                 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"})
+                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:
 
             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:
                     gpg_verify(method.name, args, auth['signature'], key)
                     return
                 except PLCAuthenticationFailure, fault:
@@ -109,7 +129,7 @@ class SessionAuth(Auth):
 
         try:
             if session['node_id'] is not None:
 
         try:
             if session['node_id'] is not None:
-                nodes = Nodes(method.api, [session['node_id']])
+                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]
@@ -120,7 +140,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})
+                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]
@@ -181,8 +201,11 @@ class BootAuth(Auth):
         # mandatory fields were present.
         assert auth.has_key('node_id')
 
         # mandatory fields were present.
         assert auth.has_key('node_id')
 
+        if 'node' not in method.roles:
+            raise PLCAuthenticationFailure, "Not allowed to call method"
+
         try:
         try:
-            nodes = Nodes(method.api, [auth['node_id']])
+            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]
@@ -214,7 +237,7 @@ class BootAuth(Auth):
                     raise PLCAuthenticationFailure, "Cannot determine IP address of requestor"
 
                 if nodenetwork['ip'] != method.source[0]:
                     raise PLCAuthenticationFailure, "Cannot determine IP address of requestor"
 
                 if nodenetwork['ip'] != method.source[0]:
-                    raise PLCAuthenticationFailure, "Requestor IP %s does not mach node IP %s" % \
+                    raise PLCAuthenticationFailure, "Requestor IP %s does not match node IP %s" % \
                           (method.source[0], nodenetwork['ip'])
             else:
                 raise PLCAuthenticationFailure, "No node key or boot nonce"
                           (method.source[0], nodenetwork['ip'])
             else:
                 raise PLCAuthenticationFailure, "No node key or boot nonce"
@@ -234,7 +257,8 @@ class BootAuth(Auth):
             method.caller = node
 
         except PLCAuthenticationFailure, fault:
             method.caller = node
 
         except PLCAuthenticationFailure, fault:
-            # XXX Send e-mail
+            if nodes:
+                notify_owners(method, node, 'authfail', include_pis = True, include_techs = True, fault = fault)
             raise fault
 
 class AnonymousAuth(Auth):
             raise fault
 
 class AnonymousAuth(Auth):
@@ -248,8 +272,10 @@ class AnonymousAuth(Auth):
             })
 
     def check(self, method, auth, *args):
             })
 
     def check(self, method, auth, *args):
-        # Sure, dude, whatever
-        return True
+        if 'anonymous' not in method.roles:
+            raise PLCAuthenticationFailure, "Not allowed to call method anonymously"
+
+        method.caller = None
 
 class PasswordAuth(Auth):
     """
 
 class PasswordAuth(Auth):
     """
@@ -258,7 +284,7 @@ class PasswordAuth(Auth):
 
     def __init__(self):
         Auth.__init__(self, {
 
     def __init__(self):
         Auth.__init__(self, {
-            'AuthMethod': Parameter(str, "Authentication method to use, typically 'password'", optional = False),
+            'AuthMethod': Parameter(str, "Authentication method to use, always 'password' or 'capability'", optional = False),
             'Username': Parameter(str, "PlanetLab username, typically an e-mail address", optional = False),
             'AuthString': Parameter(str, "Authentication string, typically a password", optional = False),
             })
             'Username': Parameter(str, "PlanetLab username, typically an e-mail address", optional = False),
             'AuthString': Parameter(str, "Authentication string, typically a password", optional = False),
             })
@@ -269,7 +295,7 @@ 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"