- don't care about Role in the auth structure anymore
[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: Auth.py,v 1.2 2006/09/08 19:44:12 mlhuang Exp $
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             })
69
70     def check(self, method, auth, *args):
71         # Method.type_check() should have checked that all of the
72         # mandatory fields were present.
73         assert auth.has_key('Username')
74
75         # Get record (must be enabled)
76         persons = Persons(method.api, [auth['Username']], enabled = True)
77         if len(persons) != 1:
78             raise PLCAuthenticationFailure, "No such account"
79
80         person = persons.values()[0]
81
82         if auth['Username'] == method.api.config.PLC_API_MAINTENANCE_USER:
83             # "Capability" authentication, whatever the hell that was
84             # supposed to mean. It really means, login as the special
85             # "maintenance user" using password authentication. Can
86             # only be used on particular machines (those in a list).
87             sources = method.api.config.PLC_API_MAINTENANCE_SOURCES.split()
88             if method.source is not None and method.source[0] not in sources:
89                 raise PLCAuthenticationFailure, "Not allowed to login to maintenance account"
90
91             # Not sure why this is not stored in the DB
92             password = method.api.config.PLC_API_MAINTENANCE_PASSWORD
93
94             if auth['AuthString'] != password:
95                 raise PLCAuthenticationFailure, "Maintenance account password verification failed"
96         else:
97             # Compare encrypted plaintext against encrypted password stored in the DB
98             plaintext = auth['AuthString'].encode(method.api.encoding)
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(plaintext, password[:12]) != password:
104                 raise PLCAuthenticationFailure, "Password verification failed"
105
106         if not set(person['roles']).intersection(method.roles):
107             raise PLCAuthenticationFailure, "Not allowed to call method"
108
109         method.caller = person