source: OpenRLabs-Git/deploy/rlabs-docker/web2py-rlabs/gluon/restricted.py @ 42095c5

mainqndtest v1.1.1
Last change on this file since 42095c5 was 42bd667, checked in by David Fuertes <dfuertes@…>, 4 years ago

Historial Limpio

  • Property mode set to 100755
File size: 10.5 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3"""
4| This file is part of the web2py Web Framework
5| Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
6| License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
7
8Restricted environment to execute application's code
9-----------------------------------------------------
10"""
11
12import sys
13from gluon._compat import pickle, ClassType, unicodeT, to_bytes
14import traceback
15import types
16import os
17import logging
18
19from gluon.storage import Storage
20from gluon.http import HTTP
21from gluon.html import BEAUTIFY, XML
22from gluon.settings import global_settings
23
24logger = logging.getLogger("web2py")
25
26__all__ = ['RestrictedError', 'restricted', 'TicketStorage', 'compile2']
27
28
29class TicketStorage(Storage):
30
31    """
32    Defines the ticket object and the default values of its members (None)
33    """
34
35    def __init__(
36        self,
37        db=None,
38        tablename='web2py_ticket'
39    ):
40        Storage.__init__(self)
41        self.db = db
42        self.tablename = tablename
43
44    def store(self, request, ticket_id, ticket_data):
45        """
46        Stores the ticket. It will figure out if this must be on disk or in db
47        """
48        if self.db:
49            self._store_in_db(request, ticket_id, ticket_data)
50        else:
51            self._store_on_disk(request, ticket_id, ticket_data)
52
53    def _store_in_db(self, request, ticket_id, ticket_data):
54        self.db._adapter.reconnect()
55        try:
56            table = self._get_table(self.db, self.tablename, request.application)
57            table.insert(ticket_id=ticket_id,
58                         ticket_data=pickle.dumps(ticket_data, pickle.HIGHEST_PROTOCOL),
59                         created_datetime=request.now)
60            self.db.commit()
61            message = 'In FILE: %(layer)s\n\n%(traceback)s\n'
62        except Exception:
63            self.db.rollback()
64            message =' Unable to store in FILE: %(layer)s\n\n%(traceback)s\n'
65        self.db.close()
66        logger.error(message % ticket_data)
67
68    def _store_on_disk(self, request, ticket_id, ticket_data):
69        ef = self._error_file(request, ticket_id, 'wb')
70        try:
71            pickle.dump(ticket_data, ef)
72        finally:
73            ef.close()
74
75    def _error_file(self, request, ticket_id, mode, app=None):
76        root = request.folder
77        if app:
78            root = os.path.join(os.path.join(root, '..'), app)
79        errors_folder = os.path.abspath(
80            os.path.join(root, 'errors'))  # .replace('\\', '/')
81        return open(os.path.join(errors_folder, ticket_id), mode)
82
83    def _get_table(self, db, tablename, app):
84        tablename = tablename + '_' + app
85        table = db.get(tablename)
86        if not table:
87            table = db.define_table(
88                tablename,
89                db.Field('ticket_id', length=100),
90                db.Field('ticket_data', 'text'),
91                db.Field('created_datetime', 'datetime'))
92        return table
93
94    def load(
95        self,
96        request,
97        app,
98        ticket_id,
99    ):
100        if not self.db:
101            try:
102                ef = self._error_file(request, ticket_id, 'rb', app)
103            except IOError:
104                return {}
105            try:
106                return pickle.load(ef)
107            finally:
108                ef.close()
109        else:
110            table = self._get_table(self.db, self.tablename, app)
111            rows = self.db(table.ticket_id == ticket_id).select()
112            return pickle.loads(rows[0].ticket_data) if rows else {}
113
114
115class RestrictedError(Exception):
116    """
117    Class used to wrap an exception that occurs in the restricted environment
118    below. The traceback is used to log the exception and generate a ticket.
119    """
120
121    def __init__(
122        self,
123        layer='',
124        code='',
125        output='',
126        environment=None,
127    ):
128        """
129        Layer here is some description of where in the system the exception
130        occurred.
131        """
132        if environment is None:
133            environment = {}
134        self.layer = layer
135        self.code = code
136        self.output = output
137        self.environment = environment
138        if layer:
139            try:
140                try:
141                    self.traceback = traceback.format_exc()
142                except:
143                    self.traceback = traceback.format_exc(limit=1)
144            except:
145                self.traceback = 'no traceback because template parsing error'
146            try:
147                self.snapshot = snapshot(context=10, code=code,
148                                         environment=self.environment)
149            except:
150                self.snapshot = {}
151        else:
152            self.traceback = '(no error)'
153            self.snapshot = {}
154
155    def log(self, request):
156        """
157        Logs the exception.
158        """
159        try:
160            d = {
161                'layer': str(self.layer),
162                'code': str(self.code),
163                'output': str(self.output),
164                'traceback': str(self.traceback),
165                'snapshot': self.snapshot,
166            }
167            ticket_storage = TicketStorage(db=request.tickets_db)
168            ticket_storage.store(request, request.uuid.split('/', 1)[1], d)
169            cmd_opts = global_settings.cmd_options
170            if cmd_opts and cmd_opts.errors_to_console:
171                logger.error(self.traceback)
172            return request.uuid
173        except:
174            logger.error(self.traceback)
175            return None
176
177
178    def load(self, request, app, ticket_id):
179        """
180        Loads a logged exception.
181        """
182        ticket_storage = TicketStorage(db=request.tickets_db)
183        d = ticket_storage.load(request, app, ticket_id)
184
185        self.layer = d.get('layer')
186        self.code = d.get('code')
187        self.output = d.get('output')
188        self.traceback = d.get('traceback')
189        self.snapshot = d.get('snapshot')
190
191    def __str__(self):
192        # safely show an useful message to the user
193        try:
194            output = self.output
195            if not isinstance(output, str, bytes, bytearray):
196                output = str(output)
197            if isinstance(output, unicodeT):
198                output = to_bytes(output)
199        except:
200            output = ""
201        return output
202
203
204def compile2(code, layer):
205    return compile(code, layer, 'exec')
206
207
208def restricted(ccode, environment=None, layer='Unknown', scode=None):
209    """
210    Runs code in environment and returns the output. If an exception occurs
211    in code it raises a RestrictedError containing the traceback. Layer is
212    passed to RestrictedError to identify where the error occurred.
213    """
214    if environment is None:
215        environment = {}
216    environment['__file__'] = layer
217    environment['__name__'] = '__restricted__'
218    try:
219        exec(ccode, environment)
220    except HTTP:
221        raise
222    except RestrictedError:
223        # do not encapsulate (obfuscate) the original RestrictedError
224        raise
225    except Exception as error:
226        # extract the exception type and value (used as output message)
227        etype, evalue, tb = sys.exc_info()
228        # XXX Show exception in Wing IDE if running in debugger
229        if __debug__ and 'WINGDB_ACTIVE' in os.environ:
230            sys.excepthook(etype, evalue, tb)
231        del tb
232        output = "%s %s" % (etype, evalue)
233        # Save source code in ticket when available
234        scode = scode if scode else ccode
235        raise RestrictedError(layer, scode, output, environment)
236
237
238def snapshot(info=None, context=5, code=None, environment=None):
239    """Return a dict describing a given traceback (based on cgitb.text)."""
240    import time
241    import linecache
242    import inspect
243    import pydoc
244    import cgitb
245
246    # if no exception info given, get current:
247    etype, evalue, etb = info or sys.exc_info()
248
249    if isinstance(etype, ClassType):
250        etype = etype.__name__
251
252    # create a snapshot dict with some basic information
253    s = {}
254    s['pyver'] = 'Python ' + sys.version.split()[0] + ': ' + sys.executable + ' (prefix: %s)' % sys.prefix
255    s['date'] = time.ctime(time.time())
256
257    # start to process frames
258    records = inspect.getinnerframes(etb, context)
259    del etb # Prevent circular references that would cause memory leaks
260    s['frames'] = []
261    for frame, file, lnum, func, lines, index in records:
262        file = file and os.path.abspath(file) or '?'
263        args, varargs, varkw, locals = inspect.getargvalues(frame)
264        call = ''
265        if func != '?':
266            call = inspect.formatargvalues(args, varargs, varkw, locals,
267                                           formatvalue=lambda value: '=' + pydoc.text.repr(value))
268
269        # basic frame information
270        f = {'file': file, 'func': func, 'call': call, 'lines': {},
271             'lnum': lnum}
272
273        highlight = {}
274
275        def reader(lnum=[lnum]):
276            highlight[lnum[0]] = 1
277            try:
278                return linecache.getline(file, lnum[0])
279            finally:
280                lnum[0] += 1
281        vars = cgitb.scanvars(reader, frame, locals)
282
283        # if it is a view, replace with generated code
284        if file.endswith('html'):
285            lmin = lnum > context and (lnum - context) or 0
286            lmax = lnum + context
287            lines = code.split("\n")[lmin:lmax]
288            index = min(context, lnum) - 1
289
290        if index is not None:
291            i = lnum - index
292            for line in lines:
293                f['lines'][i] = line.rstrip()
294                i += 1
295
296        # dump local variables (referenced in current line only)
297        f['dump'] = {}
298        for name, where, value in vars:
299            if name in f['dump']:
300                continue
301            if value is not cgitb.__UNDEF__:
302                if where == 'global':
303                    name = 'global ' + name
304                elif where != 'local':
305                    name = where + name.split('.')[-1]
306                f['dump'][name] = pydoc.text.repr(value)
307            else:
308                f['dump'][name] = 'undefined'
309
310        s['frames'].append(f)
311
312    # add exception type, value and attributes
313    s['etype'] = str(etype)
314    s['evalue'] = str(evalue)
315    s['exception'] = {}
316    if isinstance(evalue, BaseException):
317        for name in dir(evalue):
318            value = pydoc.text.repr(getattr(evalue, name))
319            s['exception'][name] = value
320
321    # add all local values (of last frame) to the snapshot
322    s['locals'] = {}
323    for name, value in locals.items():
324        s['locals'][name] = pydoc.text.repr(value)
325
326    # add web2py environment variables
327    for k, v in environment.items():
328        if k in ('request', 'response', 'session'):
329            s[k] = XML(str(BEAUTIFY(v)))
330
331    return s
Note: See TracBrowser for help on using the repository browser.