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