Changeset 250
- Timestamp:
- 07/19/07 16:09:27 (5 years ago)
- Location:
- branches/release/0.2
- Files:
-
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/release/0.2/ChangeLog
r218 r250 1 0.2.1: 2 * Fixed a serious security bug in which /screenshot prompted no auth. 3 Also fixed sending the wrong Content-Lenght for the screenshot. 4 1 5 0.2 (I've been watching you...): 2 6 * Rewrote the screenshot module completely to support active window -
branches/release/0.2/README
r219 r250 1 1 Itaka 2 Version 0.2 2 Version 0.2.1 3 3 4 4 http://itaka.jardinpresente.com.ar -
branches/release/0.2/README.Windows
r157 r250 2 2 Twisted (http://tmrc.mit.edu/mirror/twisted/Twisted/2.5/Twisted_NoDocs-2.5.0.win32-py2.5.exe). 3 3 4 You can also install all the components manually from their respective sites listed in the Requirements section. 4 You can also install all the components manually from their respective sites. 5 6 Execute itaka by double clicking the itaka.pyw file. -
branches/release/0.2/config.py
r219 r250 37 37 38 38 #: Version 39 version = "0.2 "39 version = "0.2.1" 40 40 #: Revision 41 41 revision = "$Rev$" -
branches/release/0.2/server.py
r215 r250 191 191 # Here we use our own static.Data special child resource because we need Authentication handling. 192 192 # Otherwise we would just use our own self.add_static_resource. 193 self.root = RootResource(self.gui, self.itakaglobals.headhtml + self.configuration['html']['html'] + self.itakaglobals.footerhtml) 193 194 # Also we create our unique authentication handler this keeps track if the user authenticated or not 195 self.authresource = AuthenticatedResource(self.gui) 196 self.root = RootResource(self.gui, self.authresource, self.itakaglobals.headhtml + self.configuration['html']['html'] + self.itakaglobals.footerhtml) 194 197 self.add_child_to_resource('root', '', self.root) 195 self.add_child_to_resource('root', 'screenshot', ScreenshotResource(self.gui ))198 self.add_child_to_resource('root', 'screenshot', ScreenshotResource(self.gui, self.authresource)) 196 199 self.create_site(self.root) 197 200 201 class AuthenticatedResource: 202 """ 203 Helper object to handle authentication for Resources. 204 Please read RFC 2617 to understand the HTTP Authentication process 205 """ 206 207 def __init__(self, gui_instance): 208 """ 209 Constructor that inherits code from resource.Resource->static.Data 210 211 @type gui_instance: instance 212 @param gui_instance: An instance of our L{Gui} class 213 """ 214 215 self.gui = gui_instance 216 self.configuration = self.gui.configuration 217 self.itaka_globals = self.gui.itakaglobals 218 self.noauth = self.itaka_globals.headhtml + self.configuration['html']['authfailure'] + self.itaka_globals.footerhtml 219 self.request_data_set = False 220 self.authenticated = False 221 222 def set_request_data(self, data, size, type, session_end=False): 223 """ 224 Set the information about the data we are handling 225 226 227 @type data: string 228 @param data: The data to be displayed 229 230 @type size: string 231 @param size: A string for Content-lenght 232 233 @type type: str 234 @param type: The type of data we are serving 235 236 @type session_end: bool 237 @param session_end: Whether this request is the last of a session. This deauthenticates the session. 238 """ 239 240 self.request_data_set = True 241 self.data = data 242 self.size = size 243 self.type = type 244 self.session_end = session_end 245 246 def _prompt_auth(self): 247 """ 248 Prompt the authorization dialog on the browser 249 """ 250 251 self.authenticated = False 252 self.request.setHeader('WWW-Authenticate', 'Basic realm="Itaka Screenshot Server"') 253 self.request.setResponseCode(http.UNAUTHORIZED) 254 self.request.setHeader('Content-Type', 'text/html; charset=UTF-8') 255 self.request.setHeader('Content-Length', str(len(self.noauth))) 256 self.request.setHeader('Connection', 'close') 257 258 def authenticate(self, request): 259 """ 260 Main handler for authenticated objects. 261 262 @type request: instance 263 @param request: twisted.web.server.Request instance 264 265 @rtype: bool 266 @return: Whether the user authenticated sucessfully. 267 """ 268 269 # Get up to date configuration values everytime there is a request 270 self.configuration = self.gui.configuration 271 272 self.request = request 273 self._prompt_auth() 274 self.username = self.request.getUser() 275 self.password = self.request.getPassword() 276 self.ip = self.request.getClientIP() 277 self.time = datetime.datetime.now() 278 279 if not self.username and not self.password: 280 self.gui.log.failure(('AuthenticatedResource', 'render'), ('Client provided empty username and password', 'Client %s provided empty username and password' % (self.ip)), 'WARNING') 281 self._prompt_auth() 282 else: 283 if self.username != self.configuration['server']['username'] or self.password != self.configuration['server']['password']: 284 self.gui.log.failure(('AuthenticatedResource', 'render'), ('Client provided incorrect username and password', 'Client %s provided incorrect username and password: %s:%s' % (self.ip, self.username, self.password)), 'WARNING') 285 self._prompt_auth() 286 elif self.username == self.configuration['server']['username'] and self.password == self.configuration['server']['password']: 287 self.authenticated = True 288 return self.authenticated 289 290 def return_object_data(self): 291 """ 292 Returns the data passed by set_request_data() or the default forbidden string if authentication failed. 293 294 @rtype: str 295 @return: self.data or self.noauth 296 """ 297 298 if self.request_data_set and self.authenticated: 299 self.request.setResponseCode(http.OK) 300 self.request.setHeader('Content-Type', self.type) 301 self.request.setHeader('Content-Length', self.size) 302 self.request.setHeader('Connection', 'close') 303 # Deauthenticate if it's the screenshot (last object request) 304 if self.session_end: 305 self.authenticated = False 306 self.request_data_set = True 307 return self.data 308 else: 309 # No authentication given 310 return self.noauth 311 198 312 class RootResource(static.Data): 199 313 """ … … 203 317 """ 204 318 205 def __init__(self, guiinstance, data, type='text/html; charset=UTF-8'):319 def __init__(self, guiinstance, auth_instance, data, type='text/html; charset=UTF-8'): 206 320 207 321 """ … … 211 325 @param guiinstance: An instance of our L{Gui} class. 212 326 327 @type auth_instance: AuthenticatedResource 328 @param auth_instance: An instance of our L{AuthenticatedResource} class 329 213 330 @type html: string 214 331 @param html: The HTML to be displayed. … … 219 336 220 337 self.gui = guiinstance 338 self.auth = auth_instance 221 339 self.console = self.gui.console 222 340 self.itakaglobals = self.gui.itakaglobals … … 226 344 self.children = {} 227 345 self.data = data 346 self.size = str(len(self.data)) 228 347 self.type = type 229 230 self.noauth = self.itakaglobals.headhtml + self.configuration['html']['authfailure'] + self.itakaglobals.footerhtml231 232 def _promptAuth(self):233 """234 Prompt the authorization dialog on the browser.235 """236 237 self.request.setHeader('WWW-Authenticate', 'Basic realm="Itaka Screenshot Server"')238 self.request.setResponseCode(http.UNAUTHORIZED)239 self.request.setHeader('Content-Type', self.type)240 self.request.setHeader('Connection', 'close')241 self.request.setHeader('Content-Length', str(len(self.noauth)))242 348 243 349 def render(self, request): … … 251 357 # Get up to date configuration values everytime there is a request 252 358 self.configuration = self.gui.configuration 253 254 359 self.request = request 255 self.ip = self.request.getClientIP()256 self.time = datetime.datetime.now()257 360 258 361 if self.configuration['server']['authentication']: 259 self._promptAuth() 260 self.username = self.request.getUser() 261 self.password = self.request.getPassword() 262 263 if not self.username and not self.password: 264 self.gui.log.failure(('RootResource', 'render'), ('Client provided empty username and password', 'Client %s provided empty username and password' % (self.ip)), 'WARNING') 265 self._promptAuth() 266 else: 267 if self.username != self.configuration['server']['username'] or self.password != self.configuration['server']['password']: 268 self.gui.log.failure(('RootResource', 'render'), ('Client provided incorrect username and password', 'Client %s provided incorrect username and password: %s:%s' % (self.ip, self.username, self.password)), 'WARNING') 269 self._promptAuth() 270 elif self.username == self.configuration['server']['username'] and self.password == self.configuration['server']['password']: 271 self.request.setResponseCode(http.OK) 272 self.request.setHeader('Content-Type', self.type) 273 self.request.setHeader('Connection', 'close') 274 self.request.setHeader('Content-Length', str(len(self.data))) 275 return self.data 362 if self.auth.authenticate(self.request): 363 self.auth.set_request_data(self.data, self.size, self.type) 364 return self.auth.return_object_data() 276 365 else: 277 366 self.request.setHeader('Content-Type', self.type) 367 self.request.setHeader('Content-Length', self.size) 278 368 self.request.setHeader('Connection', 'close') 279 self.request.setHeader('Content-Length', str(len(self.data)))280 369 return self.data 281 282 # No auth given283 return self.noauth284 370 285 371 class ScreenshotResource(resource.Resource): … … 288 374 """ 289 375 290 def __init__(self, guiinstance ):376 def __init__(self, guiinstance, auth_instance): 291 377 """ 292 378 Constructor. … … 294 380 @type guiinstance: instance 295 381 @param guiinstance: An instance of our L{Gui} class. 382 383 @type auth_instance: AuthenticatedResource 384 @param auth_instance: An instance of our L{AuthenticatedResource} class 296 385 """ 297 386 298 387 self.gui = guiinstance 388 self.auth = auth_instance 299 389 self.console = self.gui.console 300 390 self.itakaglobals = self.gui.itakaglobals … … 304 394 self.counter = 0 305 395 306 def render_GET(self, request): 307 """ 308 Handle GET requests for screenshot. 309 310 @type request: instance 311 @param request: twisted.web.server.Request instance. 312 313 @rtype: str 314 @return: Screenshot image. 315 """ 316 317 # Get up to date configuration values everytime there is a request 318 self.configuration = self.gui.configuration 319 320 self.request = request 396 def get_screenshot(self): 397 """ 398 Takes a screenshot and notifies the GUI. 399 """ 400 321 401 self.ip = self.request.getClientIP() 322 402 self.time = datetime.datetime.now() 323 403 324 if (self.request.uri == "/screenshot"): 404 try: 405 self.shot_file = self.screenshot.take_screenshot() 406 except error.ItakaScreenshotError, e: 407 raise error.ItakaScreenshotError, e 408 409 self.data = open(self.shot_file, 'rb').read() 410 self.size = len(self.data) 411 print self.size 412 self.counter += 1 413 414 if self.configuration['server']['notify'] and self.itaka_globals.notify_available: 415 import pynotify 416 uri = "file://" + (os.path.join(self.itaka_globals.image_dir, "itaka-take.png")) 417 418 n = pynotify.Notification('Screenshot taken', '%s requested screenshot' % (self.ip), uri) 419 420 n.set_timeout(1500) 421 n.attach_to_status_icon(self.gui.statusIcon) 422 n.show() 423 self.gui.update_gui(self.counter, self.ip, self.time) 424 425 def render_GET(self, request): 426 """ 427 Handle GET requests for screenshot 428 429 @type request: instance 430 @param request: twisted.web.server.Request instance 431 """ 432 433 # Get up to date configuration values everytime there is a request 434 self.configuration = self.gui.configuration 435 self.request = request 436 self.type = "image/" + self.configuration['screenshot']['format'] 437 438 if self.configuration['server']['authentication']: 439 if self.auth.authenticated or self.auth.authenticate(self.request): 440 try: 441 self.get_screenshot() 442 except error.ItakaScreenshotError: 443 return 444 self.auth.set_request_data(self.data, self.size, self.type, True) 445 return self.auth.return_object_data() 446 else: 325 447 try: 326 self. shotFile = self.screenshot.take_screenshot()448 self.get_screenshot() 327 449 except error.ItakaScreenshotError: 328 450 return 329 330 self.request.setHeader('Content-Type', "image/" + self.configuration['screenshot']['format']) 331 self.request.setHeader('Content-Length', str(len(self.shotFile))) 451 self.request.setHeader('Content-Type', self.type) 452 self.request.setHeader('Content-Length', self.size) 332 453 self.request.setHeader('Connection', 'close') 333 334 self.counter += 1 335 336 if self.configuration['server']['notify'] and self.itakaglobals.notifyavailable: 337 import pynotify 338 uri = "file://" + (os.path.join(self.itakaglobals.image_dir, "itaka-take.png")) 339 340 n = pynotify.Notification("Screenshot taken", 341 "%s requested screenshot" 342 % (self.ip), uri) 343 344 n.set_timeout(1500) 345 n.attach_to_status_icon(self.gui.statusIcon) 346 n.show() 347 348 self.gui.update_gui(self.counter, self.ip, self.time) 349 350 return open(self.shotFile, 'rb').read() 454 return self.data
Note: See TracChangeset
for help on using the changeset viewer.

