Initial checkin of new API implementation
[plcapi.git] / PLC / Auth.py
diff --git a/PLC/Auth.py b/PLC/Auth.py
new file mode 100644 (file)
index 0000000..2b2ea02
--- /dev/null
@@ -0,0 +1,112 @@
+#
+# PLCAPI authentication parameters
+#
+# Mark Huang <mlhuang@cs.princeton.edu>
+# Copyright (C) 2006 The Trustees of Princeton University
+#
+# $Id$
+#
+
+import crypt
+
+from PLC.Faults import *
+from PLC.Parameter import Parameter, Mixed
+from PLC.Persons import Persons
+
+class Auth(Parameter, dict):
+    """
+    Base class for all API authentication methods.
+    """
+
+    def __init__(self, auth):
+        Parameter.__init__(self, auth, "API authentication structure", False)
+        dict.__init__(auth)
+
+class NodeAuth(Auth):
+    """
+    PlanetLab version 3.x node authentication structure. Used by the
+    Boot Manager to make authenticated calls to the API based on a
+    unique node key or boot nonce value.
+    """
+
+    def __init__(self):
+        Auth.__init__(self, {
+            'AuthMethod': Parameter(str, "Authentication method to use, always 'hmac'", False),
+            'node_id': Parameter(str, "Node identifier", False),
+            'node_ip': Parameter(str, "Node primary IP address", False),
+            'value': Parameter(str, "HMAC of node key and method call", False)
+            })
+
+    def check(self, method, auth, *args):
+        # XXX Do HMAC checking
+        return True
+
+class AnonymousAuth(Auth):
+    """
+    PlanetLab version 3.x anonymous authentication structure.
+    """
+
+    def __init__(self):
+        Auth.__init__(self, {
+            'AuthMethod': Parameter(str, "Authentication method to use, always 'anonymous'", False),
+            })
+
+    def check(self, method, auth, *args):
+        # Sure, dude, whatever
+        return True
+
+class PasswordAuth(Auth):
+    """
+    PlanetLab version 3.x password authentication structure.
+    """
+
+    def __init__(self):
+        Auth.__init__(self, {
+            'AuthMethod': Parameter(str, "Authentication method to use, typically 'password'", False),
+            'Username': Parameter(str, "PlanetLab username, typically an e-mail address", False),
+            'AuthString': Parameter(str, "Authentication string, typically a password", False),
+            'Role': Parameter(str, "Role to use for this call", False)
+            })
+
+    def check(self, method, auth, *args):
+        # Method.type_check() should have checked that all of the
+        # mandatory fields were present.
+        assert auth.has_key('Username')
+
+        # Get record (must be enabled)
+        persons = Persons(method.api, [auth['Username']], enabled = True)
+        if len(persons) != 1:
+            raise PLCAuthenticationFailure, "No such account"
+
+        person = persons.values()[0]
+
+        if auth['Username'] == method.api.config.PLC_API_MAINTENANCE_USER:
+            # "Capability" authentication, whatever the hell that was
+            # supposed to mean. It really means, login as the special
+            # "maintenance user" using password authentication. Can
+            # only be used on particular machines (those in a list).
+            sources = method.api.config.PLC_API_MAINTENANCE_SOURCES.split()
+            if method.source is not None and method.source[0] not in sources:
+                raise PLCAuthenticationFailure, "Not allowed to login to maintenance account"
+
+            # Not sure why this is not stored in the DB
+            password = method.api.config.PLC_API_MAINTENANCE_PASSWORD
+
+            if auth['AuthString'] != password:
+                raise PLCAuthenticationFailure, "Maintenance account password verification failed"
+        else:
+            # Get encrypted password stored in the DB
+            password = person['password']
+
+            # Protect against blank passwords in the DB
+            if password is None or password[:12] == "" or \
+               crypt.crypt(auth['AuthString'], password[:12]) != password:
+                raise PLCAuthenticationFailure, "Password verification failed"
+
+        if auth['Role'] not in person['roles']:
+            raise PLCAuthenticationFailure, "Account does not have " + auth['Role'] + " role"
+
+        if method.roles and auth['Role'] not in method.roles:
+            raise PLCAuthenticationFailure, "Cannot call with " + auth['Role'] + "role"
+
+        method.caller = person