def edit_user(request, username=None): """ edit_user(request, username = None) -> reply @type request: HttpRequest @param request: The request object @type username: string @param username: Default to None, when creating a new user """ if request.user.username != username and not request.user.is_superuser: raise PopupException("You must be a superuser to add or edit another user.") if username is not None: instance = User.objects.get(username=username) else: instance = None if request.method == 'POST': form = UserChangeForm(request.POST, instance=instance) if form.is_valid(): # All validation rules pass if instance is None: form.save() else: # # Check for 3 more conditions: # (1) A user cannot inactivate oneself; # (2) Non-superuser cannot promote himself; and # (3) The last active superuser cannot demote/inactivate himself. # if request.user.username == username and not form.instance.is_active: raise PopupException("You cannot make yourself inactive.") global __users_lock __users_lock.acquire() try: # form.instance (and instance) now carry the new data orig = User.objects.get(username=username) if orig.is_superuser: if not form.instance.is_superuser or not form.instance.is_active: _check_remove_last_super(orig) else: if form.instance.is_superuser and not request.user.is_superuser: raise PopupException("You cannot make yourself a superuser.") # All ok form.save() finally: __users_lock.release() request.path = urlresolvers.reverse(list_users) return list_users(request) else: form = UserChangeForm(instance=instance) return render('edit_user.mako', request, dict(form=form, action=request.path, username=username))
def get_help_fs(app_name): """ Creates a local file system for a given app's help directory. """ app = appmanager.get_desktop_module(app_name) if app is not None: if app.help_dir is None: raise PopupException("No help available for app '%s'." % app_name) return LocalSubFileSystem(app.help_dir) else: raise PopupException( "App '%s' is not loaded, so no help is available for it!" % app_name)
def _upload(request): """ Handles file uploads. The uploaded file is stored in HDFS. We just need to rename it to the right path. """ if request.method == 'POST': form = UploadForm(request.POST, request.FILES) if not form.is_valid(): logger.error("Error in upload form: %s" % (form.errors, )) else: uploaded_file = request.FILES['hdfs_file'] dest = form.cleaned_data["dest"] if request.fs.isdir(dest): assert posixpath.sep not in uploaded_file.name dest = request.fs.join(dest, uploaded_file.name) # Temp file is created by superuser. Chown the file. tmp_file = uploaded_file.get_temp_path() username = request.user.username try: try: request.fs.setuser(request.fs.superuser) request.fs.chmod(tmp_file, 0644) request.fs.chown(tmp_file, username, username) except IOError, ex: msg = 'Failed to chown uploaded file ("%s") as superuser %s' % \ (tmp_file, request.fs.superuser) logger.exception(msg) raise PopupException(msg, detail=str(ex)) finally: request.fs.setuser(username) # Move the file to where it belongs try: request.fs.rename(uploaded_file.get_temp_path(), dest) except IOError, ex: raise PopupException( 'Failed to rename uploaded temporary file ("%s") to "%s": %s' % (tmp_file, dest, ex)) dest_stats = request.fs.stats(dest) return render_with_toolbars( 'upload_done.mako', request, { # status is used by "fancy uploader" 'status': 1, 'path': dest, 'result': _massage_stats(request, dest_stats), 'next': request.GET.get("next") })
def save_file(request): """ The POST endpoint to save a file in the file editor. Does the save and then redirects back to the edit page. """ form = EditorForm(request.POST) is_valid = form.is_valid() path = form.cleaned_data.get('path') if request.POST.get('save') == "saveAs": if not is_valid: return edit(request, path, form=form) else: data = dict(form=form) return render_with_toolbars("saveas.mako", request, data) if not path: raise PopupException("No path specified") if not is_valid: return edit(request, path, form=form) if request.fs.exists(path): _do_overwrite_save(request.fs, path, form.cleaned_data['contents'], form.cleaned_data['encoding']) else: _do_newfile_save(request.fs, path, form.cleaned_data['contents'], form.cleaned_data['encoding']) request.flash.put('Saved %s.' % os.path.basename(path)) """ Changing path to reflect the request path of the JFrame that will actually be returned.""" request.path = urlresolvers.reverse("filebrowser.views.edit", kwargs=dict(path=path)) return edit(request, path, form)
def _parse_fields(path, file_obj, encoding, filetypes, delimiters): """ _parse_fields(path, file_obj, encoding, filetypes, delimiters) -> (delimiter, filetype, fields_list) Go through the list of ``filetypes`` (gzip, text) and stop at the first one that works for the data. Then apply the list of ``delimiters`` and pick the most appropriate one. ``path`` is used for debugging only. Return the best delimiter, filetype and the data broken down into rows of fields. """ file_readers = [ reader for reader in FILE_READERS if reader.TYPE in filetypes ] for reader in file_readers: LOG.debug("Trying %s for file: %s" % (reader.TYPE, path)) file_obj.seek(0, hadoopfs.SEEK_SET) lines = reader.readlines(file_obj, encoding) if lines is not None: delim, fields_list = _readfields(lines, delimiters) return delim, reader.TYPE, fields_list else: # Even TextFileReader doesn't work msg = "Failed to decode file '%s' into printable characters under %s" % ( path, encoding, ) LOG.error(msg) raise PopupException(msg)
def drop_table(self, schema_name, table_name): c = self._cursor() try: c.execute("DROP TABLE %s.%s" % (schema_name, table_name)) except Exception as e: err_msg = str(e) raise PopupException(err_msg, title="DB2 Error")
def download(request, path): """ Downloads a file. This is inspired by django.views.static.serve. """ path = _unquote_path(path) if not request.fs.exists(path): raise Http404("File not found: %s" % escape(path)) if not request.fs.isfile(path): raise PopupException("'%s' is not a file" % (path, )) mimetype = mimetypes.guess_type(path)[0] or 'application/octet-stream' stats = request.fs.stats(path) mtime = stats['mtime'] size = stats['size'] if not was_modified_since(request.META.get('HTTP_IF_MODIFIED_SINCE'), mtime, size): return HttpResponseNotModified() # TODO(philip): Ideally a with statement would protect from leaks, # but tricky to do here. fh = request.fs.open(path) response = HttpResponse(_file_reader(fh), mimetype=mimetype) response["Last-Modified"] = http_date(stats['mtime']) response["Content-Length"] = stats['size'] response["Content-Disposition"] = "attachment" return response
def process_exception(self, request, exception): import traceback logging.info("Processing exception: %s: %s" % (exception, traceback.format_exc())) if hasattr(exception, "response"): return exception.response(request) if hasattr(exception, "response_data"): if request.ajax: response = render_json(exception.response_data) response[MIDDLEWARE_HEADER] = 'EXCEPTION' return response else: return render_to_response( "error.html", dict(error=exception.response_data.get("message"))) # We didn't handle it as a special exception, but if we're ajax we still # need to do some kind of nicer handling than the built-in page # Note that exception may actually be an Http404 or similar. if request.ajax: err = "An error occurred: %s" % (exception, ) logging.exception("Middleware caught an exception") return PopupException(err, detail=None).response(request) return None
def process_view(self, request, view_func, view_args, view_kwargs): """ We also perform access logging in ``process_view()`` since we have the view function, which tells us the log level. The downside is that we don't have the status code, which isn't useful for status logging anyways. """ access_log_level = getattr(view_func, 'access_log_level', None) # First, skip views not requiring login # If the view has "opted out" of login required, skip if hasattr(view_func, "login_notrequired"): log_page_hit(request, view_func, level=access_log_level or logging.DEBUG) return None # There are certain django views which are also opt-out, but # it would be evil to go add attributes to them if view_func in DJANGO_VIEW_AUTH_WHITELIST: log_page_hit(request, view_func, level=access_log_level or logging.DEBUG) return None # If user is logged in, check that he has permissions to access the # app. if request.user.is_active and request.user.is_authenticated(): AppSpecificMiddleware.augment_request_with_app(request, view_func) if request._desktop_app and \ request._desktop_app != "desktop" and \ not request.user.has_desktop_permission(action="access", app=request._desktop_app): access_log(request, 'permission denied', level=access_log_level) return PopupException( "You do not have permission to access the %s application." % (request._desktop_app.capitalize())).response(request) else: log_page_hit(request, view_func, level=access_log_level) return None logging.info("Redirecting to login page: %s", request.get_full_path()) access_log(request, 'login redirection', level=access_log_level) if request.ajax: # Send back a magic header which causes Hue.Request to interpose itself # in the ajax request and make the user login before resubmitting the # request. response = HttpResponse("/* login required */", content_type="text/javascript") response[MIDDLEWARE_HEADER] = 'LOGIN_REQUIRED' return response else: return HttpResponseRedirect( "%s?%s=%s" % (settings.LOGIN_URL, REDIRECT_FIELD_NAME, urlquote(request.get_full_path())))
def _peek_file(fs, file_form): """_peek_file(fs, file_form) -> (path, initial data)""" try: path = file_form.cleaned_data['path'] file_obj = fs.open(path) file_head = file_obj.read(IMPORT_PEEK_SIZE) file_obj.close() return (path, file_head) except IOError, ex: msg = "Failed to open file '%s': %s" % (path, ex) LOG.exception(msg) raise PopupException(msg)
def delete_user(request, username): if not request.user.is_superuser: raise PopupException("You must be a superuser to delete users.") if request.method == 'POST': try: global __users_lock __users_lock.acquire() try: user = User.objects.get(username=username) _check_remove_last_super(user) user.delete() finally: __users_lock.release() # Send a flash message saying "deleted"? return list_users(request) except User.DoesNotExist: raise PopupException("User not found.") else: return render("confirm.mako", request, dict(path=request.path, title="Delete user?"))
def _check_remove_last_super(user_obj): """Raise an error if we're removing the last superuser""" if not user_obj.is_superuser: return # Is there any other active superuser left? all_active_su = User.objects.filter(is_superuser__exact = True, is_active__exact = True) num_active_su = all_active_su.count() assert num_active_su >= 1, "No active superuser configured" if num_active_su == 1: raise PopupException("You cannot remove the last active " "superuser from the configuration.")
def view_source(request, path): """ Handle view source requests. """ gallery_name, extension = os.path.splitext(path) gallery_name = gallery_name.capitalize().replace('.', ' ').replace('_', ' ') # Load the template source filename = os.path.join(_CWD, 'templates', _GALLERY_DIR, path) try: data = format_code(extension, file(filename).read()) except OSError, ex: raise PopupException("Cannot read requested gallery template: %s" % (path, ))
def sleeper(req): seconds = False if req.REQUEST.get('delay'): seconds = int(req.REQUEST.get('delay')) if req.REQUEST.get('sleep'): seconds = int(req.REQUEST.get('sleep')) if seconds: # To prevent a DOS, only one thread can sleep at a time if not _sleep_lock.acquire(False): raise PopupException( "Only one sleep request at a time. Please try again") try: time.sleep(seconds) finally: _sleep_lock.release()
def listdir(request, path): """ Implements directory listing (or index). Intended to be called via view(). """ path = _unquote_path(path) if not request.fs.isdir(path): raise PopupException("Not a directory: %s" % (path, )) file_filter = request.REQUEST.get('file_filter', 'any') assert file_filter in ['any', 'file', 'dir'] home_dir_path = request.user.get_home_directory() data = { 'path': path, 'file_filter': file_filter, # These could also be put in automatically via # http://docs.djangoproject.com/en/dev/ref/templates/api/#django-core-context-processors-request, # but manually seems cleaner, since we only need it here. 'current_request_path': request.path, 'home_directory': request.fs.isdir(home_dir_path) and home_dir_path or None, 'cwd_set': True, 'show_upload': (request.REQUEST.get('show_upload') == 'false' and (False, ) or (True, ))[0] } stats = request.fs.listdir_stats(path) # Include parent dir, unless at filesystem root. if normpath(path) != posixpath.sep: parent_stat = request.fs.stats(posixpath.join(path, "..")) # The 'path' field would be absolute, but we want its basename to be # actually '..' for display purposes. Encode it since _massage_stats expects byte strings. parent_stat['path'] = posixpath.join(path, "..") stats.insert(0, parent_stat) data['files'] = [_massage_stats(request, stat) for stat in stats] return render_with_toolbars('listdir.mako', request, data)
def view(request, app, path): """ Views and renders a file at a given path. Markdown files are parsed through markdown; others are just pasted in <pre>'s. TODO: Expose a way to do images. """ path = _unquote_path(path) fs = get_help_fs(app) if fs.isdir(path): for i in INDEX_FILENAMES: tmp_path = os.path.join(path, i) if fs.isfile(tmp_path): path = tmp_path if not fs.isfile(path): raise PopupException("Could not find or read the file: %s (app %s)" % (path, app)) content = fs.open(path, 'r').read() content = unicode(content, 'utf-8', errors='replace') if path.lower().endswith(".md"): content = ('<div class="print rendered-markdown">' + markdown.markdown(content, ['extra']) + '</div>') elif path.lower().endswith(".html"): content = '<div class="print">%s</div>' % (content, ) else: # TODO(todd) escape content? content = '<pre>' + content + '</pre>' data = { 'content': content, 'apps': sorted([x for x in appmanager.DESKTOP_MODULES if x.help_dir], key=lambda x: x.nice_name.lower()), 'title': appmanager.get_desktop_module(app).nice_name } return render("display.mako", request, data)
def _delim_preview(fs, file_form, encoding, file_types, delimiters): """ _delim_preview(fs, file_form, encoding, file_types, delimiters) -> (fields_list, n_cols, delim_form) Look at the beginning of the file and parse it according to the list of available file_types and delimiters. """ assert file_form.is_valid() path = file_form.cleaned_data['path'] try: file_obj = fs.open(path) delim, file_type, fields_list = _parse_fields(path, file_obj, encoding, file_types, delimiters) file_obj.close() except IOError, ex: msg = "Failed to open file '%s': %s" % (path, ex) LOG.exception(msg) raise PopupException(msg)
def load_after_create(request): """ Automatically load data into a newly created table. We get here from the create's on_success_url, and expect to find ``table`` and ``path`` from the parameters. """ tablename = request.REQUEST.get('table') path = request.REQUEST.get('path') if not tablename or not path: msg = 'Internal error: Missing needed parameter to load data into table' LOG.error(msg) raise PopupException(msg) LOG.debug("Auto loading data from %s into table %s" % (path, tablename)) hql = "LOAD DATA INPATH '%s' INTO TABLE `%s`" % (path, tablename) query_msg = make_beeswax_query(request, hql) on_success_url = urlresolvers.reverse(describe_table, kwargs={'table': tablename}) return execute_directly(request, query_msg, on_success_url=on_success_url)
class SavedQuery(models.Model): """ Stores the query/report that people have save or submitted. Note that this used to be called QueryDesign. Any references to 'design' probably mean a SavedQuery. """ DEFAULT_NEW_DESIGN_NAME = 'My saved query' AUTO_DESIGN_SUFFIX = ' (new)' TYPES = (HQL, REPORT) = range(2) type = models.IntegerField(null=False) owner = models.ForeignKey(User, db_index=True) # Data is a json of dictionary. See the beeswax.design module. data = models.TextField(max_length=65536) name = models.CharField(max_length=64) desc = models.TextField(max_length=1024) mtime = models.DateTimeField(auto_now=True) # An auto design is a place-holder for things users submit but not saved. # We still want to store it as a design to allow users to save them later. is_auto = models.BooleanField(default=False, db_index=True) class Meta: ordering = ['-mtime'] def clone(self): """clone() -> A new SavedQuery with a deep copy of the same data""" design = SavedQuery(type=self.type, owner=self.owner) design.data = copy.deepcopy(self.data) design.name = copy.deepcopy(self.name) design.desc = copy.deepcopy(self.desc) design.is_auto = copy.deepcopy(self.is_auto) return design @staticmethod def get(id, owner=None, type=None): """ get(id, owner=None, type=None) -> SavedQuery object Checks that the owner and type match (when given). May raise PopupException (type/owner mismatch). May raise SavedQuery.DoesNotExist. """ try: design = SavedQuery.objects.get(id=id) except SavedQuery.DoesNotExist, err: msg = 'Cannot retrieve Beeswax design id %s' % (id, ) raise err if owner is not None and design.owner != owner: msg = 'Design id %s does not belong to user %s' % (id, owner) LOG.error(msg) raise PopupException(msg) if type is not None and design.type != type: msg = 'Type mismatch for design id %s (owner %s) - Expects %s got %s' % \ (id, owner, design.type, type) LOG.error(msg) raise PopupException(msg) return design
def error_popup_exception(request): sleeper(request) raise PopupException("This is a popup exception.", title="Hey there", detail="Some detail")
def edit(request, path, form=None): """Shows an edit form for the given path. Path does not necessarily have to exist.""" try: stats = request.fs.stats(path) except IOError, ioe: # A file not found is OK, otherwise re-raise if ioe.errno == errno.ENOENT: stats = None else: raise # Can't edit a directory if stats and stats['mode'] & stat_module.S_IFDIR: raise PopupException("Cannot edit a directory: %s" % (path, )) # Maximum size of edit if stats and stats['size'] > MAX_FILEEDITOR_SIZE: raise PopupException("File too big to edit: %s" % (path, )) if not form: encoding = request.REQUEST.get('encoding') or i18n.get_site_encoding() if stats: f = request.fs.open(path) try: try: current_contents = unicode(f.read(), encoding) except UnicodeDecodeError: raise PopupException( "File is not encoded in %s; cannot be edited: %s" %
def create_table(self, schema, table, columns, tablespace=None): c = self._cursor() try: c.execute(db2_create_table(schema, table, columns, tablespace)) except Exception as e: raise PopupException(str(e), title="DB2 Client Error")
data = format_code(extension, file(filename).read()) except OSError, ex: raise PopupException("Cannot read requested gallery template: %s" % (path, )) # Load the js references js_data = {} # map of { name: js content } yml_file = os.path.splitext(filename)[0] + '.yml' if os.path.exists(yml_file): yml = yaml.load(file(yml_file)) try: for ref in yml['js-references']: try: js_pkg, js_comp = ref.split('/') except ValueError: raise PopupException('Invalid line "%s" in file %s' % (ref, yml_file)) try: file_data = dep.get((js_pkg, js_comp)) js_data[ref] = format_code('.js', file_data.content) except: raise PopupException( 'Cannot locate "%s" package "%s" component' % (js_pkg, js_comp)) except KeyError, ex: LOG.warn('%s does not have a "js-references" section' % (yml_file, )) return render('source.mako', request, { 'data': data, 'name': gallery_name, 'js_data': js_data
def decorated(request, *args, **kwargs): if not request.user.has_desktop_permission(action, app): raise PopupException("Permission denied (%s/%s)" % (action, app)) return view_func(request, *args, **kwargs)
def display(request, path): """ Implements displaying part of a file. GET arguments are length, offset, mode, compression and encoding with reasonable defaults chosen. Note that display by length and offset are on bytes, not on characters. TODO(philip): Could easily built-in file type detection (perhaps using something similar to file(1)), as well as more advanced binary-file viewing capability (de-serialize sequence files, decompress gzipped text files, etc.). There exists a python-magic package to interface with libmagic. """ path = _unquote_path(path) if not request.fs.isfile(path): raise PopupException("Not a file: '%s'" % (path, )) stats = request.fs.stats(path) encoding = request.GET.get('encoding') or i18n.get_site_encoding() # I'm mixing URL-based parameters and traditional # HTTP GET parameters, since URL-based parameters # can't naturally be optional. # Need to deal with possibility that length is not present # because the offset came in via the toolbar manual byte entry. end = request.GET.get("end") if end: end = int(end) begin = request.GET.get("begin", 1) if begin: # Subtract one to zero index for file read begin = int(begin) - 1 if end: offset = begin length = end - begin if begin >= end: raise PopupException( "First byte to display must be before last byte to display.") else: length = int(request.GET.get("length", DEFAULT_CHUNK_SIZE_BYTES)) # Display first block by default. offset = int(request.GET.get("offset", 0)) mode = request.GET.get("mode") compression = request.GET.get("compression") if mode and mode not in ["binary", "text"]: raise PopupException("Mode must be one of 'binary' or 'text'.") if offset < 0: raise PopupException("Offset may not be less than zero.") if length < 0: raise PopupException("Length may not be less than zero.") if length > MAX_CHUNK_SIZE_BYTES: raise PopupException("Cannot request chunks greater than %d bytes" % MAX_CHUNK_SIZE_BYTES) # Auto gzip detection, unless we are explicitly told to view binary if not compression and mode != 'binary': if path.endswith('.gz') and detect_gzip(request.fs.open(path).read(2)): compression = 'gzip' offset = 0 else: compression = 'none' f = request.fs.open(path) if compression == 'gzip': if offset and offset != 0: raise PopupException( "We don't support offset and gzip Compression") try: try: contents = GzipFile('', 'r', 0, StringIO(f.read())).read(length) except: logging.warn("Could not decompress file at %s" % path, exc_info=True) contents = '' raise PopupException("Failed to decompress file") finally: f.close() else: try: f.seek(offset) contents = f.read(length) finally: f.close() # Get contents as string for text mode, or at least try uni_contents = None if not mode or mode == 'text': uni_contents = unicode(contents, encoding, errors='replace') is_binary = uni_contents.find(i18n.REPLACEMENT_CHAR) != -1 # Auto-detect mode if not mode: mode = is_binary and 'binary' or 'text' # Get contents as bytes if mode == "binary": xxd_out = list( xxd.xxd(offset, contents, BYTES_PER_LINE, BYTES_PER_SENTENCE)) dirname = posixpath.dirname(path) # Start with index-like data: data = _massage_stats(request, request.fs.stats(path)) # And add a view structure: data["success"] = True data["view"] = { 'offset': offset, 'length': length, 'end': offset + len(contents), 'dirname': dirname, 'mode': mode, 'compression': compression, 'size': stats['size'] } data["filename"] = os.path.basename(path) data["editable"] = stats['size'] < MAX_FILEEDITOR_SIZE if mode == "binary": # This might be the wrong thing for ?format=json; doing the # xxd'ing in javascript might be more compact, or sending a less # intermediate representation... logger.debug("xxd: " + str(xxd_out)) data['view']['xxd'] = xxd_out data['view']['masked_binary_data'] = False else: data['view']['contents'] = uni_contents data['view']['masked_binary_data'] = is_binary return render_with_toolbars("display.mako", request, data)