4f5ac3ccd3484c9f2b136ac364527ef97b6bdef3
[plcapi.git] / PLC / Methods / GetDummyBoxUsers.py
1 #
2 # Marta Carbone - UniPi 
3 # $Id$
4 #
5 # This Method returns a list of tuples formatted as follow:
6
7 #      <key_id> <node_ip> <slicename>
8 #
9 # and an authorized_keys file, to be used on a dummynet box.
10 #
11
12 from PLC.Method import Method                   # base class used to derive methods
13 from PLC.Parameter import Parameter, Mixed      # define input parameters
14 from PLC.Auth import Auth                       # import the Auth parameter
15 from PLC.Faults import *                        # faults library
16 from PLC.Nodes import Node, Nodes               # main class for Nodes
17 from PLC.Slices import Slice, Slices            # main class for Slices
18 from PLC.Keys import Key, Keys                  # main class for Keys
19 from PLC.Persons import Person, Persons         # main class for Persons
20 from PLC.Interfaces import *                    # get the node primary ip address
21 from PLC.NodeTags import *                      # get node connected to a dummynet box
22 from PLC.Accessors.Accessors_dummynetbox import *                       # import dummynet accessors
23
24 # authorized file delimiter string
25 NEWFILE_MARK = "authorized_keys_mark"
26
27 # Dummynet box private key
28 KEY="/usr/share/dummynet/dbox_key"
29
30 class GetDummyBoxUsers(Method):
31         """
32         Return a list of information about
33         slice, users, user keys, nodes and dummyboxes.
34
35         This Methods is mean to be used by a DummyBox.
36
37         Return keys, 0 if there are no users.
38         """
39
40         roles = ['admin', 'pi', 'node']
41
42         accepts = [
43                 Auth(),
44                 Parameter(int, 'DummyBox id'),
45         ]
46
47         returns = Parameter(str, "DummyBox files")
48
49         def call(self, auth, dummybox_id = None):
50                 """
51                 Get information about users on nodes connected to the DummyBox.
52
53                 Given a dummybox_id we get the list of connected nodes
54                 For each node_id we get a list of connected slices
55                 For each slice we get a list of connected users
56                 For each user we get a list of keys that we return to the caller.
57                 """
58
59                 # These variables contain some text to be used
60                 # to format the output files
61                 # port-forwarding should be denied in the main sshd configuration file
62                 ssh_command = "/home/user/dbox.cmd "
63                 ssh_configuration = ",no-port-forwarding,no-agent-forwarding,no-X11-forwarding "
64
65                 # check for dummynet box existence and get dummyboxes information
66                 dummyboxes = Nodes(self.api, {'node_id':dummybox_id, 'node_type':'dummynet'}, ['site_id'])
67
68                 if dummybox_id != None and not dummyboxes:
69                         raise PLCInvalidArgument, "No such DummyBox %s" % dummybox_id
70
71                 dummybox = dummyboxes[0]
72
73                 # this method needs authentication
74                 assert self.caller is not None
75
76                 # XXX check if we have rights to do this operation:
77                 #  - admins can retrive all information they want,
78                 #  - dummyboxes can retrive information regarding their site,
79                 #    nodes and slice account present on their nodes.
80
81                 # Given a dummybox_id we get the list of connected nodes
82                 connected_nodes = NodeTags(self.api, {'value': dummybox_id}, ['node_id'])
83
84                 node_list = []
85                 for i in connected_nodes:
86                         node_list.append(i['node_id'])
87
88                 nodes = Nodes(self.api, node_list, ['node_id', 'hostname', 'slice_ids'])
89                 if not nodes: return 0
90
91                 # Here nodes should be an array of dict with 'node_id', 'hostname' and 'slice_ids' fields
92
93                 user_map = "# Permission file, check here if a user can configure a link\n" # store slice-node information
94                 user_map+= "# Tuples are `owner slice_name' `hostname to reconfigure'\n"
95                 authorized_keys_dict = {}       # user's keys, dictionary
96                 dbox_key_id = 0                 # key_id used to identify users keys in the dummynetbox
97
98                 # For each node_id we get a list of connected slices
99                 for node in nodes:
100
101                         # list of connected slices
102                         slice_ids = node['slice_ids']
103                         if not slice_ids: continue
104
105                         # For each slice we get a list of connected users
106                         for slice_id in slice_ids:
107                                 # field to return
108                                 slice_name = Slices(self.api, {'slice_id': slice_id}, ['name', 'person_ids'])
109                                 if not slice_name: continue
110
111                                 # Given a slice we get a list of users 
112                                 person_ids = slice_name[0]['person_ids']        
113                                 if not person_ids: continue
114
115                                 # For each user we get a list of keys
116                                 for person_id in person_ids:
117                                         # Given a user we get a list of keys
118                                         person_list = Persons(self.api, {'person_id': person_id}, ['person_id','key_ids'])
119                                         person = person_list[0]['person_id']
120                                         key_list = person_list[0]['key_ids']
121                                         if not key_list: continue
122
123                                         for key_id in key_list:
124                                                 key = Keys(self.api, {'key_id': key_id}, ['key'])
125
126                                                 # Here we have all information we need
127                                                 # to build the authorized key file and
128                                                 # the user map file
129
130                                                 k = key[0]['key']
131
132                                                 # split the key in type/ssh_key/comment
133                                                 splitted_key = k.split(' ',2)
134                                                 uniq_key = splitted_key[0]+" "+splitted_key[1]
135
136                                                 # retrieve/create a unique dbox_key_id for this ssh_key
137                                                 if authorized_keys_dict.has_key(uniq_key):
138                                                         dbox_key_id = authorized_keys_dict[uniq_key]
139                                                 else:
140                                                         dbox_key_id+=1
141                                                         authorized_keys_dict.update({uniq_key : dbox_key_id})
142
143                                                 # get the node ip address
144                                                 nodenetworks = Interfaces(self.api, \
145                                                         {'node_id':node['node_id'], 'is_primary':'t'}, ['ip'])
146
147                                                 # append user and slice data to the user_map file       
148                                                 item = str(dbox_key_id)
149                                                 item +=" " + str(nodenetworks[0]['ip'])
150                                                 item +=" " + str(slice_name[0]['name']) + "\n"
151
152                                                 user_map += item
153
154                 # format change for authorized_keys dict
155                 authorized_keys_file="" 
156                 authorized_keys_file += "# generated automatically by GetUsersUpdate.py on the Central Site\n";
157                 authorized_keys_file += "# format file:\n";
158                 authorized_keys_file += '# command="command key_id $SSH_ORIGINAL_COMMAND",ssh_options key_type key comment\n'
159                 authorized_keys_file += "# where command, key_id and ssh_options are filled by the Central Site script\n"
160                 authorized_keys_file += "# and $SSH_ORIGINAL_COMMAND is the command line inserted by the node\n"
161                 authorized_keys_file += "\n";
162
163                 # read the central site key
164                 # the dummynet public key is located under KEY 
165                 try:
166                         pub_key=KEY+".pub"
167                         dbox_key_file = open(pub_key, 'r')
168                         dbox_key = dbox_key_file.readline()
169
170                         # upload the central site public key, used to
171                         # send plcapi commands to the central site
172                         # we use the special key_index = 0 value
173                         authorized_keys_file += "# The Central Site key, it allows to jump some checks on the dbox\n"
174                         authorized_keys_file += "command=\"" + ssh_command + "0";
175                         authorized_keys_file += " $SSH_ORIGINAL_COMMAND\"" + ssh_configuration + dbox_key + "\n"
176                         dbox_key_file.close()
177                 except:
178                         authorized_keys_file += "# The Central Site public key not found, this dummynet box\n";
179                         authorized_keys_file += "# will not accept configuration request coming from the Central Site\n";
180
181                 authorized_keys_file += "\n";
182                 # upload the users keys
183                 for i in authorized_keys_dict:
184                         # index of the key
185                         key_index = str(authorized_keys_dict[i])
186
187                         # create the dummynet box command
188                         authorized_keys_file += "command=\"" + ssh_command + key_index;
189                         authorized_keys_file += " $SSH_ORIGINAL_COMMAND\"" + ssh_configuration + str(i) + "\n"
190
191                 return user_map+NEWFILE_MARK+"\n"+authorized_keys_file