b360387697bd4bb004ff58b8cff94594cee2cd7d
[contrib/cloudeebus.git] / cloudeebus / cloudeebus.py
1 #!/usr/bin/env python
2
3 # Cloudeebus
4 #
5 # Copyright 2012 Intel Corporation.
6 #
7 # Licensed under the Apache License, Version 2.0 (the "License");
8 # you may not use this file except in compliance with the License.
9 # You may obtain a copy of the License at
10 #
11 # http://www.apache.org/licenses/LICENSE-2.0
12 #
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS,
15 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 # See the License for the specific language governing permissions and
17 # limitations under the License.
18 #
19 # Luc Yriarte <luc.yriarte@intel.com>
20 # Christophe Guiraud <christophe.guiraud@intel.com>
21 # Frederic Paut <frederic.paut@intel.com>
22 #
23
24
25 import argparse, dbus, json, sys
26
27 from twisted.internet import glib2reactor
28 # Configure the twisted mainloop to be run inside the glib mainloop.
29 # This must be done before importing the other twisted modules
30 glib2reactor.install()
31 from twisted.internet import reactor
32
33 from autobahn.websocket import listenWS
34 from autobahn.wamp import WampServerFactory, WampCraServerProtocol
35
36 from dbus.mainloop.glib import DBusGMainLoop
37
38 import gobject
39 gobject.threads_init()
40
41 from dbus import glib
42 glib.init_threads()
43
44 # enable debug log
45 from twisted.python import log
46
47 ###############################################################################
48
49 from engine import VERSION, SERVICELIST, CloudeebusService, cache
50 import engine
51
52 OPENDOOR = False
53 CREDENTIALS = {}
54 WHITELIST = []
55 NETMASK =  []
56
57 ###############################################################################
58 def ipV4ToHex(mask):
59     ## Convert an ip or an IP mask (such as ip/24 or ip/255.255.255.0) in hex value (32bits)
60     maskHex = 0
61     byte = 0
62     if mask.rfind(".") == -1:
63         if (int(mask) < 32):
64             maskHex = (2**(int(mask))-1)
65             maskHex = maskHex << (32-int(mask))
66         else:
67             raise Exception("Illegal mask (larger than 32 bits) " + mask)
68     else:
69         maskField = mask.split(".")
70         # Check if mask has four fields (byte)
71         if len(maskField) != 4:
72             raise Exception("Illegal ip address / mask (should be 4 bytes) " + mask)
73         for maskQuartet in maskField:
74             byte = int(maskQuartet)
75             # Check if each field is really a byte
76             if byte > 255:
77                 raise Exception("Illegal ip address / mask (digit larger than a byte) " + mask)              
78             maskHex += byte
79             maskHex = maskHex << 8
80         maskHex = maskHex >> 8
81     return maskHex
82
83 ###############################################################################
84 class CloudeebusServerProtocol(WampCraServerProtocol):
85     '''
86     connexion and session authentication management
87     '''
88     
89     def onSessionOpen(self):
90         # CRA authentication options
91         self.clientAuthTimeout = 0
92         self.clientAuthAllowAnonymous = OPENDOOR
93         # CRA authentication init
94         WampCraServerProtocol.onSessionOpen(self)
95     
96     
97     def getAuthPermissions(self, key, extra):
98          return {'permissions': extra.get("permissions", None),
99                  'authextra': extra.get("authextra", None),
100                  'services': extra.get("services", None)}   
101     
102     def getAuthSecret(self, key):
103         secret = CREDENTIALS.get(key, None)
104         if secret is None:
105             return None
106         # secret must be of str type to be hashed
107         return str(secret)
108     
109
110     def onAuthenticated(self, key, permissions):
111         if not OPENDOOR:
112             # check net filter
113             if NETMASK != []:
114                 ipAllowed = False
115                 for netfilter in NETMASK:
116                     ipHex=ipV4ToHex(self.peer.host)
117                     ipAllowed = (ipHex & netfilter['mask']) == netfilter['ipAllowed'] & netfilter['mask']
118                     if ipAllowed:
119                         break
120                 if not ipAllowed:
121                     raise Exception("host " + self.peer.host + " is not allowed!")
122             # check authentication key
123             if key is None:
124                 raise Exception("Authentication failed")
125             # check permissions, array.index throws exception
126             if (permissions['permissions'] != None):
127                 for req in permissions['permissions']:
128                     WHITELIST.index(req);
129             # check allowed service creation, array.index throws exception
130             if (permissions['services'] != None):
131                 for req in permissions['services']:
132                     SERVICELIST.index(req);
133         # create cloudeebus service instance
134         self.cloudeebusService = CloudeebusService(permissions)
135         # register it for RPC
136         self.registerForRpc(self.cloudeebusService)
137         # register for Publish / Subscribe
138         self.registerForPubSub("", True)
139     
140     
141     def connectionLost(self, reason):
142         WampCraServerProtocol.connectionLost(self, reason)
143         if factory.getConnectionCount() == 0:
144             cache.reset()
145
146
147
148 ###############################################################################
149
150 if __name__ == '__main__':
151     parser = argparse.ArgumentParser(description='Javascript DBus bridge.')
152     parser.add_argument('-v', '--version', action='store_true', 
153         help='print version and exit')
154     parser.add_argument('-d', '--debug', action='store_true', 
155         help='log debug info on standard output')
156     parser.add_argument('-o', '--opendoor', action='store_true',
157         help='allow anonymous access to all services')
158     parser.add_argument('-p', '--port', default='9000',
159         help='port number')
160     parser.add_argument('-c', '--credentials',
161         help='path to credentials file')
162     parser.add_argument('-w', '--whitelist',
163         help='path to whitelist file (DBus services to use)')
164     parser.add_argument('-s', '--servicelist',
165         help='path to servicelist file (DBus services to export)')
166     parser.add_argument('-n', '--netmask',
167         help='netmask,IP filter (comma separated.) eg. : -n 127.0.0.1,192.168.2.0/24,10.12.16.0/255.255.255.0')
168     
169     args = parser.parse_args(sys.argv[1:])
170
171     if args.version:
172         print("Cloudeebus version " + VERSION)
173         exit(0)
174     
175     if args.debug:
176         log.startLogging(sys.stdout)
177     
178     OPENDOOR = args.opendoor
179     
180     if args.credentials:
181         jfile = open(args.credentials)
182         CREDENTIALS = json.load(jfile)
183         jfile.close()
184     
185     if args.whitelist:
186         jfile = open(args.whitelist)
187         WHITELIST.extend(json.load(jfile))
188         jfile.close()
189         
190     if args.servicelist:
191         jfile = open(args.servicelist)
192         SERVICELIST.extend(json.load(jfile))
193         jfile.close()
194         
195     if args.netmask:
196         iplist = args.netmask.split(",")
197         for ip in iplist:
198             if ip.rfind("/") != -1:
199                 ip=ip.split("/")
200                 ipAllowed = ip[0]
201                 mask = ip[1]
202             else:
203                 ipAllowed = ip
204                 mask = "255.255.255.255" 
205             NETMASK.append( {'ipAllowed': ipV4ToHex(ipAllowed), 'mask' : ipV4ToHex(mask)} )
206
207     uri = "ws://localhost:" + args.port
208     
209     factory = WampServerFactory(uri, debugWamp = args.debug)
210     factory.protocol = CloudeebusServerProtocol
211     factory.setProtocolOptions(allowHixie76 = True)
212
213     # Configure engine for WAMP.
214     engine.factory = factory
215     engine.OPENDOOR = OPENDOOR
216
217     listenWS(factory)
218     
219     DBusGMainLoop(set_as_default=True)
220     
221     reactor.run()