do we really want to limit credentials to just users? authorities need them too.
[sfa.git] / geni / util / auth.py
1 #
2 # GeniAPI authentication 
3 #
4 #
5
6 import time
7 from geni.util.faults import *
8 from geni.util.excep import *
9 from geni.util.credential import Credential
10 from geni.util.trustedroot import TrustedRootList
11 from geni.util.hierarchy import Hierarchy
12 from geni.util.rights import RightList
13 from geni.util.genitable import *
14 from geni.util.config import *
15
16 class Auth:
17     """
18     Credential based authentication
19     """
20
21     def __init__(self, peer_cert = None, config = None ):
22         self.peer_cert = peer_cert
23         self.hierarchy = Hierarchy()
24         self.trusted_cert_list = TrustedRootList().get_list() 
25         if not config:
26             self.config = Config() 
27     
28
29     def check(self, cred, operation):
30         """
31         Check the credential against the peer cert (callerGID included 
32         in the credential matches the caller that is connected to the 
33         HTTPS connection, check if the credential was signed by a 
34         trusted cert and check if the credential is allowd to perform 
35         the specified operation.    
36         """
37         self.client_cred = Credential(string = cred)
38         self.client_gid = self.client_cred.get_gid_caller()
39         self.object_gid = self.client_cred.get_gid_object()
40         
41         # make sure the client_gid is not blank
42         if not self.client_gid:
43             raise MissingCallerGID(self.client_cred.get_subject())
44
45         # make sure the client_gid matches client's certificate
46         peer_cert = self.peer_cert
47         if not peer_cert.is_pubkey(self.client_gid.get_pubkey()):
48             raise ConnectionKeyGIDMismatch(self.client_gid.get_subject())
49
50         # make sure the client is allowed to perform the operation
51         if operation:
52             if not self.client_cred.can_perform(operation):
53                 raise InsufficientRights(operation)
54
55         if self.trusted_cert_list:
56             self.client_cred.verify_chain(self.trusted_cert_list)
57             if self.client_gid:
58                 self.client_gid.verify_chain(self.trusted_cert_list)
59             if self.object_gid:
60                 self.object_gid.verify_chain(self.trusted_cert_list)
61
62         return True
63
64         
65     def get_auth_info(self, auth_hrn):
66         """
67         Given an authority name, return the information for that authority.
68         This is basically a stub that calls the hierarchy module.
69         
70         @param auth_hrn human readable name of authority  
71         """
72
73         return self.hierarchy.get_auth_info(auth_hrn)
74
75
76     def get_auth_table(self, auth_name):
77         """
78         Given an authority name, return the database table for that authority.
79         If the databse table does not exist, then one will be automatically
80         created.
81
82         @param auth_name human readable name of authority
83         """
84         auth_info = self.get_auth_info(auth_name)
85         table = GeniTable(hrn=auth_name,
86                           cninfo=auth_info.get_dbinfo())
87         # if the table doesn't exist, then it means we haven't put any records
88         # into this authority yet.
89
90         if not table.exists():
91             print >> log, "Registry: creating table for authority", auth_name
92             table.create()
93     
94         return table
95
96     def veriry_auth_belongs_to_me(self, name):
97         """
98         Verify that an authority belongs to our hierarchy. 
99         This is basically left up to the implementation of the hierarchy
100         module. If the specified name does not belong, ane exception is 
101         thrown indicating the caller should contact someone else.
102
103         @param auth_name human readable name of authority
104         """
105
106         self.get_auth_info(name)
107
108
109     def verify_object_belongs_to_me(self, name):
110         """
111         Verify that an object belongs to our hierarchy. By extension,
112         this implies that the authority that owns the object belongs
113         to our hierarchy. If it does not an exception is thrown.
114     
115         @param name human readable name of object        
116         """
117         auth_name = self.get_authority(name)
118         if not auth_name:
119             # the root authority belongs to the registry by default?
120             # TODO: is this true?
121             return
122         self.verify_auth_belongs_to_me(auth_name) 
123              
124     def verify_auth_belongs_to_me(self, name):
125         # get auth info will throw an exception if the authority doesnt exist
126         self.get_auth_info(name) 
127
128
129     def verify_object_permission(self, name):
130         """
131         Verify that the object gid that was specified in the credential
132         allows permission to the object 'name'. This is done by a simple
133         prefix test. For example, an object_gid for plc.arizona would 
134         match the objects plc.arizona.slice1 and plc.arizona.
135     
136         @param name human readable name to test  
137         """
138         object_hrn = self.object_gid.get_hrn()
139         if object_hrn == name:
140             return
141         if name.startswith(object_hrn + "."):
142             return
143         raise PermissionError(name)
144
145     def determine_user_rights(self, src_cred, record):
146         """
147         Given a user credential and a record, determine what set of rights the
148         user should have to that record.
149
150         Src_cred can be None when obtaining a user credential, but should be
151         set to a valid user credential when obtaining a slice or authority
152         credential.
153
154         This is intended to replace determine_rights() and
155         verify_cancreate_credential()
156         """
157
158         type = record.get_type()
159         if src_cred:
160             cred_object_hrn = src_cred.get_gid_object().get_hrn()
161         else:
162             # supplying src_cred==None is only valid when obtaining user
163             # credentials.
164             #assert(type == "user")
165             
166             cred_object_hrn = None
167
168         rl = RightList()
169
170         if type=="slice":
171             researchers = record.get_geni_info().get("researcher", [])
172             if (cred_object_hrn in researchers):
173                 rl.add("refresh")
174                 rl.add("embed")
175                 rl.add("bind")
176                 rl.add("control")
177                 rl.add("info")
178
179         elif type == "authority":
180             pis = record.get_geni_info().get("pi", [])
181             operators = record.get_geni_info().get("operator", [])
182             if (cred_object_hrn in pis):
183                 rl.add("sa")
184             if (cred_object_hrn in operators):
185                 rl.add("ma")
186             if (cred_object_hrn in pis) or (cred_object_hrn in operators):
187                 rl.add("authority")
188
189         elif type == "user":
190             rl.add("refresh")
191             rl.add("resolve")
192             rl.add("info")
193
194         return rl
195
196     def verify_cancreate_credential(self, src_cred, record):
197         """
198         Verify that a user can retrive a particular type of credential.
199         For slices, the user must be on the researcher list. For SA and
200         MA the user must be on the pi and operator lists respectively
201         """
202
203         type = record.get_type()
204         cred_object_hrn = src_cred.get_gid_object().get_hrn()
205         if cred_object_hrn in [self.config.GENI_REGISTRY_ROOT_AUTH]:
206             return
207         if type=="slice":
208             researchers = record.get_geni_info().get("researcher", [])
209             if not (cred_object_hrn in researchers):
210                 raise PermissionError(cred_object_hrn + " is not in researcher list for " + record.get_name())
211         elif type == "sa":
212             pis = record.get_geni_info().get("pi", [])
213             if not (cred_object_hrn in pis):
214                 raise PermissionError(cred_object_hrn + " is not in pi list for " + record.get_name())
215         elif type == "ma":
216             operators = record.get_geni_info().get("operator", [])
217             if not (cred_object_hrn in operators):
218                 raise PermissionError(cred_object_hrn + " is not in operator list for " + record.get_name())
219
220     def get_leaf(self, hrn):
221         parts = hrn.split(".")
222         return ".".join(parts[-1:])
223
224     def get_authority(self, hrn):
225         parts = hrn.split(".")
226         return ".".join(parts[:-1])
227
228     def hrn_to_pl_slicename(self, hrn):
229         parts = hrn.split(".")
230         return parts[-2] + "_" + parts[-1]
231
232     # assuming hrn is the hrn of an authority, return the plc authority name
233     def hrn_to_pl_authname(self, hrn):
234         parts = hrn.split(".")
235         return parts[-1]
236
237     # assuming hrn is the hrn of an authority, return the plc login_base
238     def hrn_to_pl_login_base(self, hrn):
239         return self.hrn_to_pl_authname(hrn)
240