def settings_view(): settings = AccountService.get_settings(current_user.get_id()) form = make_settings_form(settings) def render_form(): return render_template("settings.tpl", settings=settings, form=form) if form.is_submitted(): if not form.validate(): flash_update_error() return render_form() new_password = form.new_password.data if new_password: if current_user["password"] == new_password: flash_update_error(form.new_password, "Same as current password") return render_form() AccountService.update(current_user.get_id(), AccountInterface(password=new_password)) # Use the UserSettings class in order to update/set settings user_settings.set(current_user.get_id(), form.data) flash("Success! Your settings have been saved.") return render_form()
def link(user_id: int, subject: Optional[str]): """ Link the given account to the given openid subject :param user_id: user ID to link :param subject: openid subject to link to the account :return: """ AccountService.update(user_id, AccountInterface(hash=subject))
def delete_hits(): form = AdminDeleteForm() if form.validate(): hit_threshold = form.hit_threshold.data delete_queue = () # If admin has selected to delete from all keys if form.all_keys.data: # Select all files, where hits <= threshold. delete_queue = db.fetchall( "SELECT * FROM `files` WHERE `hits` <= %s", [hit_threshold] ) else: # Select files from a user, where hits <= threshold user = AccountService.get_by_key(form.key.data) if user: delete_queue = db.fetchall( "SELECT * FROM `files` WHERE `userid` = %s AND `hits` <= %s", [user.get_id(), hit_threshold], ) size, count, _ = FileService.delete_batch(delete_queue) return render_template( "delete.tpl", messages=[ f"{count} items deleted. {functions.sizeof_fmt(size)} of disk space saved." ], ) flash("There was an error processing your input.", "error") return redirect("/admin")
def gallery_auth(user_key): # Set a long cookie to grant a user access to a gallery form = GalleryAuthForm() if form.validate_on_submit(): user = AccountService.get_by_key(user_key) resp = redirect(f"/gallery/{user_key}") GalleryService.set_auth_cookie(resp, user.get_id(), form.authcode.data, form.remember.data) return resp form.flash_errors() return render_template("gallery_auth.tpl", form=form)
def get_or_create_account(self): try: user, is_authed = AccountService.get_or_create_account( self.key.data, self.password.data ) if not user or not is_authed: self.key.errors.append("Key or password is incorrect") except Error as e: self.key.errors.append(e.error) return None, False return user, is_authed
def callback(): if "error" in request.args: flash("There was a problem logging you in.", "error") logout_user() else: token = oauth.auth.authorize_access_token() id_token = oauth.auth.parse_id_token(token) pprint(dict(token=token, user=id_token)) subject = id_token.get("sub") if subject: user = AccountService.get_by_subject(subject) if user: LoginForm.login(user) else: # subject is not assigned to an account already # go through the account linking flow session["oauth"] = id_token return redirect(url_for("oauth.link")) return redirect(url_for("static.login"))
def validate_auth_cookie(user_id: int, settings: Optional[dict] = None) -> bool: """ Validate a gallery auth cookie for the given user. :param user_id: ID of the user :param settings: user settings dict (pass in if already grabbed) :return: True if the auth cookie is valid or not needed, else False """ if not settings: settings = AccountService.get_settings(user_id) get_user_setting = partial(get_dict, settings) if get_user_setting("block.value") and get_user_setting( "gallery_password.value" ): auth_cookie = GalleryService.get_auth_cookie(user_id) hex_pass = hashlib.sha1( get_user_setting("gallery_password.value").encode("utf-8") ).hexdigest() if not auth_cookie or not hex_pass == auth_cookie: return False return True
def api_upload_file(upload_type="file"): if upload_type not in ["file", "paste"]: # The type the user provided doesn't exist. raise json_error("This upload type does not exist") # support login thru the API key = request.form.get("key") password = request.form.get("password") if key and password and not current_user.is_authenticated: user, is_authed = AccountService.get_or_create_account(key, password) if user and is_authed: login_user(user) else: raise json_error("Incorrect key or password") user_id = current_user.get_id() is_file = upload_type == "file" file: Optional[Union[FileInterface, PasteInterface]] = None if is_file: file = submit_file(user_id) elif upload_type == "paste": file = submit_paste(user_id) if not file: raise json_error("Failed to upload file") shorturl = file["shorturl"] ext = file["ext"] if is_file else "paste" host = get_host() path = "/" + ("" if is_file else upload_type + "/") return json_response( type=ext, key="anon" if not user_id else current_user["key"], base=host, url=path + shorturl, full_url=host + path + shorturl, )
def get_potential_name(userinfo, errors=None): """ Get the potential account name for the given openid userinfo. :param userinfo: openid userinfo :param errors: list of errors to append to :return: tuple of (name, errors) """ if not errors: errors = [] subject = userinfo["sub"] potential_name = userinfo.get("preferred_username", subject) existing_user = AccountService.get_by_key(potential_name) if existing_user: # user with potential name exists already, use subject instead errors.append( f'User "{potential_name}" already exists, please authenticate as them or choose another username' ) potential_name = subject # trim to 30 chars to fit in db # TODO: update key/password db length? potential_name = potential_name[:30] return potential_name, errors
def gallery_view(user_key=None): form_filter = GallerySortForm(request.args) case = form_filter.case.data query = form_filter.query.data if form_filter.validate(): page = form_filter.page.data query_in = form_filter.in_.data active_sort = form_filter.sort.data entry_filter = FileType(form_filter.filter.data) else: # return a generic error if validation failed return render_template("error.tpl", error="There were no results for this search.") user = AccountService.get_by_key(user_key) # anonymous account, hide gallery if not user or user.get_id() == 0: return render_template("error.tpl", error="Specified key does not exist.") # Check the users settings to see if they have specified # gallery access restrictions settings = AccountService.get_settings(user.get_id()) if not GalleryService.validate_auth_cookie(user.get_id(), settings=settings): # if cookie , show password incorrect message if GalleryService.get_auth_cookie(user.get_id()): flash("Incorrect gallery password", "error") return redirect(f"/gallery/auth/{user_key}") search_in = search_modes[query_in][1] sort_by = sort_modes[active_sort] # where clauses sql_search = [] # parameters for the where clauses params = [] if query: # Ensure case-sensitive searching with a binary conversion collate = " COLLATE utf8_bin" if case else "" sql_search.append(f"`{search_in}`{collate} LIKE %s") params.append("%" + query + "%") # filter results by file type if entry_filter != FileType.ALL: sql_search.append(f"`type` = %s") params.append(entry_filter.value) # function that creates a search query given a "what" clause. # used to get the total file count, then later # use the same query to get the files themselves def get_search_query(sub): return f"SELECT {sub} FROM `files` WHERE {' AND '.join(sql_search + [''])} `userid` = %s ORDER BY `{sort_by[1]}` {sort_by[2]}" params.append(user.get_id()) total_entries = db.fetchone(get_search_query("COUNT(`id`) AS `total`"), params)["total"] # generate pages with a useful pagination class pagination = functions.Pagination(page, 30, total_entries) if page > pagination.pages: if pagination.pages == 0: return render_template( "error.tpl", error="There were no results for this search.") else: return render_template("error.tpl", error="This page does not exist.") # fetch the files given a pagination limit (only fetch files for given page) results: Tuple[FileInterface, ...] = db.fetchall( f"{get_search_query('*')} LIMIT {pagination.limit}", params) files = [] for row in results: file = { "url": row["shorturl"], "hits": row["hits"], "time": { "epoch": row["date"].timestamp(), "timestamp": row["date"].strftime("%d/%m/%Y @ %H:%M:%S"), }, "ext": row["ext"], "original": row["original"], "type": FileType(row["type"]), } if file["type"] == FileType.PASTE: paste: PasteInterface = PasteService.get_by_id(row["original"]) file["content"] = paste["content"] file["name"] = paste["name"] or row["shorturl"] file["size"] = len(file["content"].split("\n")) # get latest revision for paste latest_rev = PasteService.get_latest_revision(paste) if latest_rev: file["content"] = latest_rev["paste"] # add latest commit hash to url file["url"] = row["shorturl"] + ":" + latest_rev["commit"] else: file["size"] = functions.sizeof_fmt(row["size"]) if file["type"] == FileType.IMAGE: file["resolution"] = (row["width"], row["height"]) files.append(file) usage = None tally = db.fetchone(get_search_query("SUM(`size`) AS `total`"), params) if tally and tally.get("total"): usage = functions.sizeof_fmt(float(tally["total"])) form_delete = GalleryDeleteForm() return render_template( "gallery.tpl", key=user_key, pages=pagination, usage=usage, entries=total_entries, active_sort=active_sort, search={ "query": query, "in": query_in, "case": case }, files=files, xhr=request.headers.get("X-AJAX", "false") == "true", show_ext=get_dict(settings, "ext.value"), hl=functools.partial(functions.highlight_text, search=query, case_sensitive=case), form_delete=form_delete, form_filter=form_filter, url_for_page=url_for_page, )