This commit was manufactured by cvs2svn to create branch
[plcapi.git] / PLC / Auth.py
index bb62c09..a062b3a 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.10 2007/01/04 16:01:28 mlhuang Exp $
+# $Id: Auth.py,v 1.15 2007/02/01 22:28:48 mlhuang Exp $
 #
 
 import crypt
 #
 
 import crypt
@@ -18,21 +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.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):
     """
@@ -72,7 +92,7 @@ class GPGAuth(Auth):
             for key in keys:
                 try:
                     from PLC.GPG import gpg_verify
             for key in keys:
                 try:
                     from PLC.GPG import gpg_verify
-                    gpg_verify(method.name, args, auth['signature'], key)
+                    gpg_verify(args, key, auth['signature'], method.name)
                     return
                 except PLCAuthenticationFailure, fault:
                     pass
                     return
                 except PLCAuthenticationFailure, fault:
                     pass
@@ -181,6 +201,9 @@ 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:
             nodes = Nodes(method.api, {'node_id': auth['node_id'], 'peer_id': None})
             if not nodes:
         try:
             nodes = Nodes(method.api, {'node_id': auth['node_id'], 'peer_id': None})
             if not nodes:
@@ -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, 'peer_id': None})
+        persons = Persons(method.api, {'email': auth['Username'].lower(), 'enabled': True, 'peer_id': None})
         if len(persons) != 1:
             raise PLCAuthenticationFailure, "No such account"
 
         if len(persons) != 1:
             raise PLCAuthenticationFailure, "No such account"
 
@@ -300,6 +326,6 @@ class PasswordAuth(Auth):
                 raise PLCAuthenticationFailure, "Password verification failed"
 
         if not set(person['roles']).intersection(method.roles):
                 raise PLCAuthenticationFailure, "Password verification failed"
 
         if not set(person['roles']).intersection(method.roles):
-            raise PLCAuthenticationFailure, "Not allowed to call method"
+           raise PLCAuthenticationFailure, "Not allowed to call method"
 
         method.caller = person
 
         method.caller = person