OpenSecurity/bin/opensecurity_client_restful_server.py
author Oliver Maurhart <oliver.maurhart@ait.ac.at>
Tue, 29 Apr 2014 13:00:46 +0200
changeset 134 f1c1c06c947d
parent 130 f770f1b2abf7
child 136 ac117cd7bab1
permissions -rwxr-xr-x
keyfile query and dialog handling reworked
     1 #!/bin/env python
     2 # -*- coding: utf-8 -*-
     3 
     4 # ------------------------------------------------------------
     5 # opensecurity_client_restful_server
     6 # 
     7 # the OpenSecurity client RESTful server
     8 #
     9 # Autor: Oliver Maurhart, <oliver.maurhart@ait.ac.at>
    10 #
    11 # Copyright (C) 2013 AIT Austrian Institute of Technology
    12 # AIT Austrian Institute of Technology GmbH
    13 # Donau-City-Strasse 1 | 1220 Vienna | Austria
    14 # http://www.ait.ac.at
    15 #
    16 # This program is free software; you can redistribute it and/or
    17 # modify it under the terms of the GNU General Public License
    18 # as published by the Free Software Foundation version 2.
    19 # 
    20 # This program is distributed in the hope that it will be useful,
    21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
    22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    23 # GNU General Public License for more details.
    24 # 
    25 # You should have received a copy of the GNU General Public License
    26 # along with this program; if not, write to the Free Software
    27 # Foundation, Inc., 51 Franklin Street, Fifth Floor, 
    28 # Boston, MA  02110-1301, USA.
    29 # ------------------------------------------------------------
    30 
    31 
    32 # ------------------------------------------------------------
    33 # imports
    34 
    35 import json
    36 import os
    37 import os.path
    38 import subprocess
    39 import sys
    40 import urllib
    41 import urllib2
    42 import web
    43 import threading
    44 import time
    45 
    46 # local
    47 from environment import Environment
    48 
    49 
    50 # ------------------------------------------------------------
    51 # const
    52 
    53 
    54 __version__ = "0.2.5"
    55 
    56 
    57 """All the URLs we know mapping to class handler"""
    58 opensecurity_urls = (
    59     '/credentials',             'os_credentials',
    60     '/keyfile',                 'os_keyfile',
    61     '/notification',            'os_notification',
    62     '/password',                'os_password',
    63     '/',                        'os_root'
    64 )
    65 
    66 
    67 # ------------------------------------------------------------
    68 # code
    69 
    70 
    71 class os_credentials:
    72 
    73     """OpenSecurity '/credentials' handler.
    74     
    75     This is called on GET /credentials?text=TEXT.
    76     Ideally this should pop up a user dialog to insert his
    77     credentials based the given TEXT.
    78     """
    79     
    80     def GET(self):
    81         
    82         # pick the arguments
    83         args = web.input()
    84         
    85         # we _need_ a text
    86         if not "text" in args:
    87             raise web.badrequest('no text given')
    88         
    89         # remember remote ip
    90         remote_ip = web.ctx.environ['REMOTE_ADDR']
    91 
    92         # create the process which queries the user
    93         dlg_image = os.path.join(sys.path[0], 'opensecurity_dialog.pyw')
    94         process_command = [sys.executable, dlg_image, 'credentials', args.text]
    95         process = subprocess.Popen(process_command, shell = False, stdout = subprocess.PIPE)        
    96         
    97         # run process result handling in seprate thread (not to block main one)
    98         bouncer = UserResultBouncer(process, remote_ip, '/credentials')
    99         bouncer.start()
   100          
   101         return 'user queried for credentials'
   102 
   103 
   104 class os_keyfile:
   105 
   106     """OpenSecurity '/keyfile' handler.
   107     
   108     This is called on GET /keyfile?text=TEXT.
   109     Ideally this should pop up a user dialog to insert his
   110     password along with a keyfile.
   111     """
   112     
   113     def GET(self):
   114         
   115         # pick the arguments
   116         args = web.input()
   117         
   118         # we _need_ a text
   119         if not "text" in args:
   120             raise web.badrequest('no text given')
   121             
   122         # remember remote ip
   123         remote_ip = web.ctx.environ['REMOTE_ADDR']
   124         
   125         # create the process which queries the user
   126         dlg_image = os.path.join(sys.path[0], 'opensecurity_dialog.pyw')
   127         process_command = [sys.executable, dlg_image, 'keyfile', args.text]
   128         process = subprocess.Popen(process_command, shell = False, stdout = subprocess.PIPE)        
   129         
   130         # run process result handling in seprate thread (not to block main one)
   131         bouncer = UserResultBouncer(process, remote_ip, '/keyfile')
   132         bouncer.start()
   133          
   134         return 'user queried for password and keyfile'
   135 
   136 
   137 class os_notification:
   138 
   139     """OpenSecurity '/notification' handler.
   140     
   141     This is called on GET /notification?msgtype=TYPE&text=TEXT.
   142     This will pop up an OpenSecurity notifcation window
   143     """
   144     
   145     def GET(self):
   146         
   147         # pick the arguments
   148         args = web.input()
   149         
   150         # we _need_ a type
   151         if not "msgtype" in args:
   152             raise web.badrequest('no msgtype given')
   153             
   154         if not args.msgtype in ['information', 'warning', 'critical']:
   155             raise web.badrequest('Unknown value for msgtype')
   156             
   157         # we _need_ a text
   158         if not "text" in args:
   159             raise web.badrequest('no text given')
   160             
   161         # invoke the user dialog as a subprocess
   162         dlg_image = os.path.join(sys.path[0], 'opensecurity_dialog.py')
   163         process_command = [sys.executable, dlg_image, 'notification-' + args.msgtype, args.text]
   164         process = subprocess.Popen(process_command, shell = False, stdout = subprocess.PIPE)
   165 
   166         return "Ok"
   167 
   168 
   169 class os_password:
   170 
   171     """OpenSecurity '/password' handler.
   172     
   173     This is called on GET /password?text=TEXT.
   174     Ideally this should pop up a user dialog to insert his
   175     password based device name.
   176     """
   177     
   178     def GET(self):
   179         
   180         # pick the arguments
   181         args = web.input()
   182         
   183         # we _need_ a text
   184         if not "text" in args:
   185             raise web.badrequest('no text given')
   186             
   187         # remember remote ip
   188         remote_ip = web.ctx.environ['REMOTE_ADDR']
   189         
   190         # create the process which queries the user
   191         dlg_image = os.path.join(sys.path[0], 'opensecurity_dialog.pyw')
   192         process_command = [sys.executable, dlg_image, 'password', args.text]
   193         process = subprocess.Popen(process_command, shell = False, stdout = subprocess.PIPE)        
   194         
   195         # run process result handling in seprate thread (not to block main one)
   196         bouncer = UserResultBouncer(process, remote_ip, '/password')
   197         bouncer.start()
   198         
   199         return 'user queried for password'
   200 
   201 
   202 class os_root:
   203 
   204     """OpenSecurity '/' handler"""
   205     
   206     def GET(self):
   207     
   208         res = "OpenSecurity-Client RESTFul Server { \"version\": \"%s\" }" % __version__
   209         
   210         # add some sample links
   211         res = res + """
   212         
   213 USAGE EXAMPLES:
   214         
   215 Request a password: 
   216     (copy paste this into your browser's address field after the host:port)
   217     
   218     /password?text=Give+me+a+password+for+device+%22My+USB+Drive%22+(ID%3A+32090-AAA-X0)
   219     
   220     (eg.: http://127.0.0.1:8090/password?text=Give+me+a+password+for+device+%22My+USB+Drive%22+(ID%3A+32090-AAA-X0))
   221     NOTE: check yout taskbar, the dialog window may not pop up in front of your browser window.
   222     
   223     
   224 Request a combination of user and password:
   225     (copy paste this into your browser's address field after the host:port)
   226     
   227     /credentials?text=Tell+the+NSA+which+credentials+to+use+in+order+to+avoid+hacking+noise+on+wire.
   228     
   229     (eg.: http://127.0.0.1:8090/credentials?text=Tell+the+NSA+which+credentials+to+use+in+order+to+avoid+hacking+noise+on+wire.)
   230     NOTE: check yout taskbar, the dialog window may not pop up in front of your browser window.
   231     
   232 
   233 Request a combination of password and keyfile:
   234     (copy paste this into your browser's address field after the host:port)
   235     
   236     /keyfile?text=Your%20private%20RSA%20Keyfile%3A
   237     
   238     (eg.: http://127.0.0.1:8090//keyfile?text=Your%20private%20RSA%20Keyfile%3A)
   239     NOTE: check yout taskbar, the dialog window may not pop up in front of your browser window.
   240     
   241 
   242 Start a Browser:
   243     (copy paste this into your browser's address field after the host:port)
   244 
   245     /application?vm=Debian+7&app=Browser
   246 
   247     (e.g. http://127.0.0.1:8090/application?vm=Debian+7&app=Browser)
   248         """
   249     
   250         return res
   251 
   252 
   253 class UserResultBouncer(threading.Thread):
   254 
   255     """A class to spawn off user I/O waiting and push the data back to the requesting services."""
   256 
   257     def __init__(self, process, remote_ip, resource): 
   258 
   259         """ctor"""
   260 
   261         threading.Thread.__init__(self)
   262         self._process = process
   263         self._remote_ip = remote_ip
   264         self._resource = resource
   265  
   266     
   267     def stop(self):
   268 
   269         """stop thread"""
   270         self.running = False
   271         
   272     
   273     def run(self):
   274 
   275         """run the thread"""
   276 
   277         # invoke the user dialog as a subprocess
   278         result = self._process.communicate()[0]
   279         if self._process.returncode != 0:
   280             print 'user request has been aborted.'
   281             return
   282         
   283         # all ok, tell send request back appropriate destination
   284         try:
   285             j = json.loads(result)
   286         except:
   287             print 'error in password parsing'
   288             return
   289         
   290         # TODO: it would be WAY easier and secure if we just 
   291         #       add the result json to a HTTP-POST here.
   292         url_addr = 'http://' + self._remote_ip + ':58080' + self._resource
   293         url = url_addr + '?' + urllib.urlencode(j)
   294         req = urllib2.Request(url)
   295         try:
   296             res = urllib2.urlopen(req)
   297         except:
   298             print 'failed to contact: ' + url_addr
   299             return 
   300 
   301 
   302 # start
   303 if __name__ == "__main__":
   304     server = web.application(opensecurity_urls, globals())
   305     server.run()
   306