Initial checkin of new API implementation
[plcapi.git] / PLC / Auth.py
1 #
2 # PLCAPI authentication parameters
3 #
4 # Mark Huang <mlhuang@cs.princeton.edu>
5 # Copyright (C) 2006 The Trustees of Princeton University
6 #
7 # $Id$
8 #
9
10 import crypt
11
12 from PLC.Faults import *
13 from PLC.Parameter import Parameter, Mixed
14 from PLC.Persons import Persons
15
16 class Auth(Parameter, dict):
17     """
18     Base class for all API authentication methods.
19     """
20
21     def __init__(self, auth):
22         Parameter.__init__(self, auth, "API authentication structure", False)
23         dict.__init__(auth)
24
25 class NodeAuth(Auth):
26     """
27     PlanetLab version 3.x node authentication structure. Used by the
28     Boot Manager to make authenticated calls to the API based on a
29     unique node key or boot nonce value.
30     """
31
32     def __init__(self):
33         Auth.__init__(self, {
34             'AuthMethod': Parameter(str, "Authentication method to use, always 'hmac'", False),
35             'node_id': Parameter(str, "Node identifier", False),
36             'node_ip': Parameter(str, "Node primary IP address", False),
37             'value': Parameter(str, "HMAC of node key and method call", False)
38             })
39
40     def check(self, method, auth, *args):
41         # XXX Do HMAC checking
42         return True
43
44 class AnonymousAuth(Auth):
45     """
46     PlanetLab version 3.x anonymous authentication structure.
47     """
48
49     def __init__(self):
50         Auth.__init__(self, {
51             'AuthMethod': Parameter(str, "Authentication method to use, always 'anonymous'", False),
52             })
53
54     def check(self, method, auth, *args):
55         # Sure, dude, whatever
56         return True
57
58 class PasswordAuth(Auth):
59     """
60     PlanetLab version 3.x password authentication structure.
61     """
62
63     def __init__(self):
64         Auth.__init__(self, {
65             'AuthMethod': Parameter(str, "Authentication method to use, typically 'password'", False),
66             'Username': Parameter(str, "PlanetLab username, typically an e-mail address", False),
67             'AuthString': Parameter(str, "Authentication string, typically a password", False),
68             'Role': Parameter(str, "Role to use for this call", False)
69             })
70
71     def check(self, method, auth, *args):
72         # Method.type_check() should have checked that all of the
73         # mandatory fields were present.
74         assert auth.has_key('Username')
75
76         # Get record (must be enabled)
77         persons = Persons(method.api, [auth['Username']], enabled = True)
78         if len(persons) != 1:
79             raise PLCAuthenticationFailure, "No such account"
80
81         person = persons.values()[0]
82
83         if auth['Username'] == method.api.config.PLC_API_MAINTENANCE_USER:
84             # "Capability" authentication, whatever the hell that was
85             # supposed to mean. It really means, login as the special
86             # "maintenance user" using password authentication. Can
87             # only be used on particular machines (those in a list).
88             sources = method.api.config.PLC_API_MAINTENANCE_SOURCES.split()
89             if method.source is not None and method.source[0] not in sources:
90                 raise PLCAuthenticationFailure, "Not allowed to login to maintenance account"
91
92             # Not sure why this is not stored in the DB
93             password = method.api.config.PLC_API_MAINTENANCE_PASSWORD
94
95             if auth['AuthString'] != password:
96                 raise PLCAuthenticationFailure, "Maintenance account password verification failed"
97         else:
98             # Get encrypted password stored in the DB
99             password = person['password']
100
101             # Protect against blank passwords in the DB
102             if password is None or password[:12] == "" or \
103                crypt.crypt(auth['AuthString'], password[:12]) != password:
104                 raise PLCAuthenticationFailure, "Password verification failed"
105
106         if auth['Role'] not in person['roles']:
107             raise PLCAuthenticationFailure, "Account does not have " + auth['Role'] + " role"
108
109         if method.roles and auth['Role'] not in method.roles:
110             raise PLCAuthenticationFailure, "Cannot call with " + auth['Role'] + "role"
111
112         method.caller = person