again getting away with caller_cred and passing just the hrn of the initial caller
[sfa.git] / sfa / methods / update.py
1 ### $Id$
2 ### $URL$
3
4 import time
5 from sfa.util.faults import *
6 from sfa.util.method import Method
7 from sfa.util.parameter import Parameter, Mixed
8 from sfa.trust.auth import Auth
9 from sfa.util.record import GeniRecord
10 from sfa.util.genitable import GeniTable
11 from sfa.trust.certificate import Keypair, convert_public_key
12 from sfa.trust.gid import *
13 from sfa.util.debug import log
14 from sfa.trust.credential import Credential
15
16 class update(Method):
17     """
18     Update an object in the registry. Currently, this only updates the
19     PLC information associated with the record. The Geni fields (name, type,
20     GID) are fixed.
21     
22     @param cred credential string specifying rights of the caller
23     @param record a record dictionary to be updated
24
25     @return 1 if successful, faults otherwise 
26     """
27
28     interfaces = ['registry']
29     
30     accepts = [
31         Parameter(str, "Credential string"),
32         Parameter(dict, "Record dictionary to be updated"),
33         Mixed(Parameter(str, "Request hash"),
34               Parameter(None, "Request hash not specified"))
35         ]
36
37     returns = Parameter(int, "1 if successful")
38     
39     def call(self, cred, record_dict, request_hash=None, origin_hrn=None):
40         if origin_hrn==None:
41                 origin_hrn=Credential(string=cred).get_gid_caller().get_hrn()
42
43             #log the call
44         self.api.logger.info("interface: %s\tcaller-hrn: %s\ttarget-hrn: %s\tmethod-name: %s"%(self.api.interface, origin_hrn, None, self.name))
45         # This cred might be an authority cred, not a user, so we cant use it to 
46         # authenticate the caller's request_hash. Let just get the caller's gid
47         # from the cred and authenticate using that
48         client_gid = Credential(string=cred).get_gid_caller()
49         client_gid_str = client_gid.save_to_string(save_parents=True)
50         self.api.auth.authenticateGid(client_gid_str, [cred], request_hash)
51         self.api.auth.check(cred, "update")
52         new_record = GeniRecord(dict = record_dict)
53         type = new_record['type']
54         hrn = new_record['hrn']
55         self.api.auth.verify_object_permission(hrn)
56         table = GeniTable()
57         # make sure the record exists
58         records = table.findObjects({'type': type, 'hrn': hrn})
59         if not records:
60             raise RecordNotFound(hrn)
61         record = records[0]
62         record['last_updated'] = time.gmtime()
63          
64         # Update_membership needs the membership lists in the existing record
65         # filled in, so it can see if members were added or removed
66         self.api.fill_record_info(record)
67
68          # Use the pointer from the existing record, not the one that the user
69         # gave us. This prevents the user from inserting a forged pointer
70         pointer = record['pointer']
71
72         # update the PLC information that was specified with the record
73
74         if (type == "authority"):
75             self.api.plshell.UpdateSite(self.api.plauth, pointer, new_record)
76
77         elif type == "slice":
78             pl_record=self.api.geni_fields_to_pl_fields(type, hrn, new_record)
79             if 'name' in pl_record:
80                 pl_record.pop('name')
81             self.api.plshell.UpdateSlice(self.api.plauth, pointer, pl_record)
82
83         elif type == "user":
84             # SMBAKER: UpdatePerson only allows a limited set of fields to be
85             #    updated. Ideally we should have a more generic way of doing
86             #    this. I copied the field names from UpdatePerson.py...
87             update_fields = {}
88             all_fields = new_record
89             for key in all_fields.keys():
90                 if key in ['first_name', 'last_name', 'title', 'email',
91                            'password', 'phone', 'url', 'bio', 'accepted_aup',
92                            'enabled']:
93                     update_fields[key] = all_fields[key]
94             self.api.plshell.UpdatePerson(self.api.plauth, pointer, update_fields)
95
96             if 'key' in new_record and new_record['key']:
97                 # must check this key against the previous one if it exists
98                 persons = self.api.plshell.GetPersons(self.api.plauth, [pointer], ['key_ids'])
99                 person = persons[0]
100                 keys = person['key_ids']
101                 keys = self.api.plshell.GetKeys(self.api.plauth, person['key_ids'])
102                 key_exists = False
103                 if isinstance(new_record['key'], list):
104                     new_key = new_record['key'][0]
105                 else:
106                     new_key = new_record['key']
107   
108                 # Delete all stale keys
109                 for key in keys:
110                     if new_record['key'] != key['key']:
111                         self.api.plshell.DeleteKey(self.api.plauth, key['key_id'])
112                     else:
113                         key_exists = True
114                 if not key_exists:
115                     self.api.plshell.AddPersonKey(self.api.plauth, pointer, {'key_type': 'ssh', 'key': new_key})
116
117                 # update the openssl key and gid
118                 pkey = convert_public_key(new_key)
119                 uuid = create_uuid()
120                 gid_object = self.api.auth.hierarchy.create_gid(hrn, uuid, pkey)
121                 gid = gid_object.save_to_string(save_parents=True)
122                 record['gid'] = gid
123                 record = GeniRecord(dict=record)
124                 table.update(record)
125                  
126         elif type == "node":
127             self.api.plshell.UpdateNode(self.api.plauth, pointer, new_record)
128
129         else:
130             raise UnknownGeniType(type)
131
132         # update membership for researchers, pis, owners, operators
133         self.api.update_membership(record, new_record)
134
135         return 1