def reset_password(token): """ Method to allow a user to reset his/her password. """ form = forms.ResetPasswordForm() user_obj = pagure.lib.search_user(SESSION, token=token) if not user_obj: flask.flash("No user associated with this token.", "error") return flask.redirect(flask.url_for("auth_login")) elif not user_obj.token: flask.flash("Invalid user, this user never asked for a password change", "error") return flask.redirect(flask.url_for("auth_login")) if form.validate_on_submit(): password = "******" % (form.password.data, APP.config.get("PASSWORD_SEED", None)) user_obj.password = hashlib.sha512(password).hexdigest() user_obj.token = None SESSION.add(user_obj) try: SESSION.commit() flask.flash("Password changed") except SQLAlchemyError as err: SESSION.rollback() flask.flash("Could not set the new password.", "error") APP.logger.debug("Password lost change - Error setting password.") APP.logger.exception(err) return flask.redirect(flask.url_for("auth_login")) return flask.render_template("login/password_reset.html", form=form, token=token)
def _check_session_cookie(): """ Set the user into flask.g if the user is logged in. """ cookie_name = APP.config.get('SESSION_COOKIE_NAME', 'pagure') cookie_name = '%s_local_cookie' % cookie_name session_id = None user = None login_time = None if cookie_name and cookie_name in flask.request.cookies: sessionid = flask.request.cookies.get(cookie_name) session = pagure.lib.login.get_session_by_visitkey( SESSION, sessionid) if session and session.user: now = datetime.datetime.now() if now > session.expiry: flask.flash('Session timed-out', 'error') elif APP.config.get('CHECK_SESSION_IP', True) \ and session.user_ip != flask.request.remote_addr: flask.flash('Session expired', 'error') else: new_expiry = now + datetime.timedelta(days=30) session_id = session.visit_key user = session.user login_time = session.created session.expiry = new_expiry SESSION.add(session) try: SESSION.commit() except SQLAlchemyError, err: # pragma: no cover flask.flash( 'Could not prolong the session in the db, ' 'please report this error to an admin', 'error') APP.logger.exception(err)
def update_project(repo, username=None): """ Update the description of a project. """ if admin_session_timedout(): flask.flash('Action canceled, try it again', 'error') url = flask.url_for( 'view_settings', username=username, repo=repo.name) return flask.redirect( flask.url_for('auth_login', next=url)) repo = pagure.lib.get_project(SESSION, repo, user=username) if not repo: flask.abort(404, 'Project not found') if not is_repo_admin(repo): flask.abort( 403, 'You are not allowed to change the settings for this project') form = pagure.forms.ProjectFormSimplified() if form.validate_on_submit(): try: repo.description = form.description.data repo.avatar_email = form.avatar_email.data.strip() repo.url = form.url.data.strip() SESSION.add(repo) SESSION.commit() flask.flash('Project updated') except SQLAlchemyError, err: # pragma: no cover SESSION.rollback() flask.flash(str(err), 'error')
def new_user(): """ Create a new user. """ form = forms.NewUserForm() if form.validate_on_submit(): username = form.user.data if pagure.lib.search_user(SESSION, username=username): flask.flash('Username already taken.', 'error') return flask.redirect(flask.request.url) email = form.email_address.data if pagure.lib.search_user(SESSION, email=email): flask.flash('Email address already taken.', 'error') return flask.redirect(flask.request.url) password = '******' % ( form.password.data, APP.config.get('PASSWORD_SEED', None)) form.password.data = hashlib.sha512(password).hexdigest() token = pagure.lib.login.id_generator(40) user = model.User() user.token = token form.populate_obj(obj=user) user.default_email = form.email_address.data SESSION.add(user) SESSION.flush() emails = [email.email for email in user.emails] if form.email_address.data not in emails: useremail = model.UserEmail( user_id=user.id, email=form.email_address.data) SESSION.add(useremail) SESSION.flush() try: SESSION.flush() send_confirmation_email(user) flask.flash( 'User created, please check your email to activate the ' 'account') except SQLAlchemyError as err: SESSION.rollback() flask.flash('Could not create user.') APP.logger.debug('Could not create user.') APP.logger.exception(err) SESSION.commit() return flask.redirect(flask.url_for('auth_login')) return flask.render_template( 'login/user_new.html', form=form, )
def request_pull_edit(repo, requestid, username=None): """ Edit the title of a pull-request. """ repo = pagure.lib.get_project(SESSION, repo, user=username) if not repo: flask.abort(404, 'Project not found') if not repo.settings.get('pull_requests', True): flask.abort(404, 'No pull-requests found for this project') request = pagure.lib.search_pull_requests( SESSION, project_id=repo.id, requestid=requestid) if not request: flask.abort(404, 'Pull-request not found') if request.status != 'Open': flask.abort(400, 'Pull-request is already closed') if not is_repo_admin(repo) \ and flask.g.fas_user.username != request.user.username: flask.abort(403, 'You are not allowed to edit this pull-request') form = pagure.forms.RequestPullForm() if form.validate_on_submit(): request.title = form.title.data.strip() request.initial_comment = form.initial_comment.data.strip() SESSION.add(request) try: SESSION.commit() flask.flash('Request pull edited!') except SQLAlchemyError as err: # pragma: no cover SESSION.rollback() APP.logger.exception(err) flask.flash( 'Could not edit this pull-request in the database', 'error') return flask.redirect(flask.url_for( 'request_pull', username=username, repo=repo.name, requestid=requestid)) elif flask.request.method == 'GET': form.title.data = request.title form.initial_comment.data = request.initial_comment return flask.render_template( 'pull_request_title.html', select='requests', request=request, repo=repo, username=username, form=form, )
def change_password(): """ Method to change the password for local auth users. """ form = forms.ChangePasswordForm() user_obj = pagure.lib.search_user( SESSION, username=flask.g.fas_user.username) if not user_obj: flask.abort(404, 'User not found') if form.validate_on_submit(): try: password_checks = check_password( form.old_password.data, user_obj.password, seed=APP.config.get('PASSWORD_SEED', None)) except pagure.exceptions.PagureException as err: APP.logger.exception(err) flask.flash( 'Could not update your password, either user or password ' 'could not be checked', 'error') return flask.redirect(flask.url_for('auth_login')) if password_checks: user_obj.password = generate_hashed_value(form.password.data) SESSION.add(user_obj) else: flask.flash( 'Could not update your password, either user or password ' 'could not be checked', 'error') return flask.redirect(flask.url_for('auth_login')) try: SESSION.commit() flask.flash( 'Password changed') except SQLAlchemyError as err: # pragma: no cover SESSION.rollback() flask.flash('Could not set the new password.', 'error') APP.logger.debug( 'Password change - Error setting new password.') APP.logger.exception(err) return flask.redirect(flask.url_for('auth_login')) return flask.render_template( 'login/password_recover.html', form=form, )
def do_login(): """ Lo the user in user. """ form = forms.LoginForm() next_url = flask.request.args.get('next_url') if not next_url or next_url == 'None': next_url = flask.url_for('index') if form.validate_on_submit(): username = form.username.data password = '******' % ( form.password.data, APP.config.get('PASSWORD_SEED', None)) password = hashlib.sha512(password).hexdigest() user_obj = pagure.lib.search_user(SESSION, username=username) if not user_obj or user_obj.password != password: flask.flash('Username or password invalid.', 'error') return flask.redirect(flask.url_for('auth_login')) elif user_obj.token: flask.flash( 'Invalid user, did you confirm the creation with the url ' 'provided by email?', 'error') return flask.redirect(flask.url_for('auth_login')) else: visit_key = pagure.lib.login.id_generator(40) now = datetime.datetime.utcnow() expiry = now + datetime.timedelta(days=30) session = model.PagureUserVisit( user_id=user_obj.id, user_ip=flask.request.remote_addr, visit_key=visit_key, expiry=expiry, ) SESSION.add(session) try: SESSION.commit() flask.g.fas_user = user_obj flask.g.fas_session_id = visit_key flask.g.fas_user.login_time = now flask.flash('Welcome %s' % user_obj.username) except SQLAlchemyError as err: # pragma: no cover flask.flash( 'Could not set the session in the db, ' 'please report this error to an admin', 'error') APP.logger.exception(err) return flask.redirect(next_url) else: flask.flash('Insufficient information provided', 'error') return flask.redirect(flask.url_for('auth_login'))
def new_user(): """ Create a new user. """ form = forms.NewUserForm() if form.validate_on_submit(): username = form.user.data if pagure.lib.search_user(SESSION, username=username): flask.flash('Username already taken.', 'error') return flask.redirect(flask.request.url) email = form.email_address.data if pagure.lib.search_user(SESSION, email=email): flask.flash('Email address already taken.', 'error') return flask.redirect(flask.request.url) form.password.data = generate_hashed_value(form.password.data) token = pagure.lib.login.id_generator(40) user = model.User() user.token = token form.populate_obj(obj=user) user.default_email = form.email_address.data SESSION.add(user) SESSION.flush() pagure.lib.add_email_to_user( SESSION, user, form.email_address.data) try: SESSION.commit() send_confirmation_email(user) flask.flash( 'User created, please check your email to activate the ' 'account') except SQLAlchemyError as err: # pragma: no cover SESSION.rollback() flask.flash('Could not create user.') APP.logger.debug('Could not create user.') APP.logger.exception(err) return flask.redirect(flask.url_for('auth_login')) return flask.render_template( 'login/user_new.html', form=form, )
def lost_password(): """ Method to allow a user to change his/her password assuming the email is not compromised. """ form = forms.LostPasswordForm() if form.validate_on_submit(): username = form.username.data user_obj = pagure.lib.search_user(SESSION, username=username) if not user_obj: flask.flash('Username invalid.', 'error') return flask.redirect(flask.url_for('auth_login')) elif user_obj.token: current_time = datetime.datetime.utcnow() invalid_period = user_obj.updated_on + \ datetime.timedelta(minutes=3) if current_time < invalid_period: flask.flash( 'An email was sent to you less than 3 minutes ago, ' 'did you check your spam folder? Otherwise, ' 'try again after some time.', 'error') return flask.redirect(flask.url_for('auth_login')) token = pagure.lib.login.id_generator(40) user_obj.token = token SESSION.add(user_obj) try: SESSION.commit() send_lostpassword_email(user_obj) flask.flash( 'Check your email to finish changing your password') except SQLAlchemyError as err: # pragma: no cover SESSION.rollback() flask.flash( 'Could not set the token allowing changing a password.', 'error') APP.logger.debug('Password lost change - Error setting token.') APP.logger.exception(err) return flask.redirect(flask.url_for('auth_login')) return flask.render_template( 'login/password_change.html', form=form, )
def confirm_user(token): """ Confirm a user account. """ user_obj = pagure.lib.search_user(SESSION, token=token) if not user_obj: flask.flash('No user associated with this token.', 'error') else: user_obj.token = None SESSION.add(user_obj) try: SESSION.commit() flask.flash('Email confirmed, account activated') return flask.redirect(flask.url_for('auth_login')) except SQLAlchemyError, err: # pragma: no cover flask.flash( 'Could not set the account as active in the db, ' 'please report this error to an admin', 'error') APP.logger.exception(err)
def lost_password(): """ Method to allow a user to change his/her password assuming the email is not compromised. """ form = forms.LostPasswordForm() if form.validate_on_submit(): username = form.username.data user_obj = pagure.lib.search_user(SESSION, username=username) if not user_obj: flask.flash('Username invalid.', 'error') return flask.redirect(flask.url_for('auth_login')) elif user_obj.token: flask.flash( 'Invalid user, did you confirm the creation with the url ' 'provided by email? Or did you already ask for a password ' 'change?', 'error') return flask.redirect(flask.url_for('auth_login')) token = pagure.lib.login.id_generator(40) user_obj.token = token SESSION.add(user_obj) try: SESSION.commit() send_lostpassword_email(user_obj) flask.flash( 'Check your email to finish changing your password') except SQLAlchemyError as err: SESSION.rollback() flask.flash( 'Could not set the token allowing changing a password.', 'error') APP.logger.debug('Password lost change - Error setting token.') APP.logger.exception(err) return flask.redirect(flask.url_for('auth_login')) return flask.render_template( 'login/password_change.html', form=form, )
def request_pull_edit(repo, requestid, username=None): """ Edit the title of a pull-request. """ repo = pagure.lib.get_project(SESSION, repo, user=username) if not repo: flask.abort(404, "Project not found") if not repo.settings.get("pull_requests", True): flask.abort(404, "No pull-requests found for this project") request = pagure.lib.search_pull_requests(SESSION, project_id=repo.id, requestid=requestid) if not request: flask.abort(404, "Pull-request not found") if request.status != "Open": flask.abort(400, "Pull-request is already closed") if not is_repo_admin(repo) and flask.g.fas_user.username != request.user.username: flask.abort(403, "You are not allowed to edit this pull-request") form = pagure.forms.RequestPullForm() if form.validate_on_submit(): request.title = form.title.data SESSION.add(request) try: SESSION.commit() flask.flash("Request pull edited!") except SQLAlchemyError as err: # pragma: no cover SESSION.rollback() APP.logger.exception(err) flask.flash("Could not edit this pull-request in the database", "error") return flask.redirect(flask.url_for("request_pull", username=username, repo=repo.name, requestid=requestid)) elif flask.request.method == "GET": form.title.data = request.title return flask.render_template( "pull_request_title.html", select="requests", request=request, repo=repo, username=username, form=form )
def reset_password(token): """ Method to allow a user to reset his/her password. """ form = forms.ResetPasswordForm() user_obj = pagure.lib.search_user(SESSION, token=token) if not user_obj: flask.flash('No user associated with this token.', 'error') return flask.redirect(flask.url_for('auth_login')) elif not user_obj.token: flask.flash( 'Invalid user, this user never asked for a password change', 'error') return flask.redirect(flask.url_for('auth_login')) if form.validate_on_submit(): user_obj.password = generate_hashed_value(form.password.data) user_obj.token = None SESSION.add(user_obj) try: SESSION.commit() flask.flash( 'Password changed') except SQLAlchemyError as err: # pragma: no cover SESSION.rollback() flask.flash('Could not set the new password.', 'error') APP.logger.debug( 'Password lost change - Error setting password.') APP.logger.exception(err) return flask.redirect(flask.url_for('auth_login')) return flask.render_template( 'login/password_reset.html', form=form, token=token, )
def _check_session_cookie(): """ Set the user into flask.g if the user is logged in. """ cookie_name = APP.config.get('SESSION_COOKIE_NAME', 'pagure') cookie_name = '%s_local_cookie' % cookie_name session_id = None user = None login_time = None if cookie_name and cookie_name in flask.request.cookies: sessionid = flask.request.cookies.get(cookie_name) session = pagure.lib.login.get_session_by_visitkey( SESSION, sessionid) if session and session.user: now = datetime.datetime.now() if now > session.expiry: flask.flash('Session timed-out', 'error') elif APP.config.get('CHECK_SESSION_IP', True) \ and session.user_ip != flask.request.remote_addr: flask.flash('Session expired', 'error') else: new_expiry = now + datetime.timedelta(days=30) session_id = session.visit_key user = session.user login_time = session.created session.expiry = new_expiry SESSION.add(session) try: SESSION.commit() except SQLAlchemyError as err: # pragma: no cover flask.flash( 'Could not prolong the session in the db, ' 'please report this error to an admin', 'error') APP.logger.exception(err) flask.g.fas_session_id = session_id flask.g.fas_user = user if user: flask.g.fas_user.login_time = login_time
def update_project(repo, username=None): """ Update the description of a project. """ if admin_session_timedout(): flask.flash('Action canceled, try it again', 'error') url = flask.url_for( 'view_settings', username=username, repo=repo) return flask.redirect( flask.url_for('auth_login', next=url)) repo = pagure.lib.get_project(SESSION, repo, user=username) if not repo: flask.abort(404, 'Project not found') if not is_repo_admin(repo): flask.abort( 403, 'You are not allowed to change the settings for this project') form = pagure.forms.ProjectFormSimplified() if form.validate_on_submit(): try: repo.description = form.description.data repo.avatar_email = form.avatar_email.data.strip() repo.url = form.url.data.strip() pagure.lib.update_tags( SESSION, repo, tags=[t.strip() for t in form.tags.data.split(',')], username=flask.g.fas_user.username, ticketfolder=None, redis=None) SESSION.add(repo) SESSION.commit() flask.flash('Project updated') except SQLAlchemyError, err: # pragma: no cover SESSION.rollback() flask.flash(str(err), 'error')
def update_project(repo, username=None): """ Update the description of a project. """ if admin_session_timedout(): flask.flash("Action canceled, try it again", "error") url = flask.url_for("view_settings", username=username, repo=repo) return flask.redirect(flask.url_for("auth_login", next=url)) repo = pagure.lib.get_project(SESSION, repo, user=username) if not repo: flask.abort(404, "Project not found") if not is_repo_admin(repo): flask.abort(403, "You are not allowed to change the settings for this project") form = pagure.forms.ProjectFormSimplified() if form.validate_on_submit(): try: repo.description = form.description.data repo.avatar_email = form.avatar_email.data.strip() repo.url = form.url.data.strip() pagure.lib.update_tags( SESSION, repo, tags=[t.strip() for t in form.tags.data.split(",")], username=flask.g.fas_user.username, ticketfolder=None, ) SESSION.add(repo) SESSION.commit() flask.flash("Project updated") except SQLAlchemyError as err: # pragma: no cover SESSION.rollback() flask.flash(str(err), "error") return flask.redirect(flask.url_for("view_settings", username=username, repo=repo.name))
def new_issue(repo, username=None, namespace=None): """ Create a new issue """ repo = flask.g.repo if not repo.settings.get('issue_tracker', True): flask.abort(404, 'No issue tracker found for this project') form = pagure.forms.IssueFormSimplied() if form.validate_on_submit(): title = form.title.data content = form.issue_content.data private = form.private.data try: user_obj = pagure.lib.get_user( SESSION, flask.g.fas_user.username) except pagure.exceptions.PagureException: flask.abort( 404, 'No such user found in the database: %s' % ( flask.g.fas_user.username)) try: issue = pagure.lib.new_issue( SESSION, repo=repo, title=title, content=content, private=private or False, user=flask.g.fas_user.username, ticketfolder=APP.config['TICKETS_FOLDER'], ) SESSION.commit() # If there is a file attached, attach it. filestream = flask.request.files.get('filestream') if filestream and '<!!image>' in issue.content: new_filename = pagure.lib.git.add_file_to_git( repo=repo, issue=issue, ticketfolder=APP.config['TICKETS_FOLDER'], user=user_obj, filename=filestream.filename, filestream=filestream.stream, ) # Replace the <!!image> tag in the comment with the link # to the actual image filelocation = flask.url_for( 'view_issue_raw_file', repo=repo.name, username=username, filename=new_filename, ) new_filename = new_filename.split('-', 1)[1] url = '[![%s](%s)](%s)' % ( new_filename, filelocation, filelocation) issue.content = issue.content.replace('<!!image>', url) SESSION.add(issue) SESSION.commit() return flask.redirect(flask.url_for( '.view_issue', username=username, repo=repo.name, namespace=namespace, issueid=issue.id)) except pagure.exceptions.PagureException as err: flask.flash(str(err), 'error') except SQLAlchemyError as err: # pragma: no cover SESSION.rollback() flask.flash(str(err), 'error') types = None default = None ticketrepopath = os.path.join(APP.config['TICKETS_FOLDER'], repo.path) if os.path.exists(ticketrepopath): ticketrepo = pygit2.Repository(ticketrepopath) if not ticketrepo.is_empty and not ticketrepo.head_is_unborn: commit = ticketrepo[ticketrepo.head.target] # Get the different ticket types files = __get_file_in_tree( ticketrepo, commit.tree, ['templates'], bail_on_tree=True) if files: types = [f.name.rstrip('.md') for f in files] # Get the default template default_file = __get_file_in_tree( ticketrepo, commit.tree, ['templates', 'default.md'], bail_on_tree=True) if default_file: default, _ = pagure.doc_utils.convert_readme( default_file.data, 'md') if flask.request.method == 'GET': form.private.data = repo.settings.get( 'issues_default_to_private', False) return flask.render_template( 'new_issue.html', select='issues', form=form, repo=repo, username=username, types=types, default=default, )
def update_issue(repo, issueid, username=None, namespace=None): ''' Add a comment to an issue. ''' is_js = flask.request.args.get('js', False) repo = flask.g.repo if flask.request.method == 'GET': if not is_js: flask.flash('Invalid method: GET', 'error') return flask.redirect(flask.url_for( 'view_issue', username=username, repo=repo.name, namespace=repo.namespace, issueid=issueid)) if not repo.settings.get('issue_tracker', True): flask.abort(404, 'No issue tracker found for this project') issue = pagure.lib.search_issues(SESSION, repo, issueid=issueid) if issue is None or issue.project != repo: flask.abort(404, 'Issue not found') if issue.private and not flask.g.repo_admin \ and (not authenticated() or not issue.user.user == flask.g.fas_user.username): flask.abort( 403, 'This issue is private and you are not allowed to view it') if flask.request.form.get('edit_comment'): commentid = flask.request.form.get('edit_comment') form = pagure.forms.EditCommentForm() if form.validate_on_submit(): return edit_comment_issue( repo.name, issueid, commentid, username=username) status = pagure.lib.get_issue_statuses(SESSION) form = pagure.forms.UpdateIssueForm( status=status, priorities=repo.priorities, milestones=repo.milestones, close_status=repo.close_status, ) if form.validate_on_submit(): repo_admin = flask.g.repo_admin if flask.request.form.get('drop_comment'): commentid = flask.request.form.get('drop_comment') comment = pagure.lib.get_issue_comment( SESSION, issue.uid, commentid) if comment is None or comment.issue.project != repo: flask.abort(404, 'Comment not found') if (flask.g.fas_user.username != comment.user.username or comment.parent.status != 'Open') \ and not flask.g.repo_admin: flask.abort( 403, 'You are not allowed to remove this comment from ' 'this issue') issue.last_updated = datetime.datetime.utcnow() SESSION.add(issue) SESSION.delete(comment) try: SESSION.commit() if not is_js: flask.flash('Comment removed') except SQLAlchemyError as err: # pragma: no cover is_js = False SESSION.rollback() LOG.error(err) if not is_js: flask.flash( 'Could not remove the comment: %s' % commentid, 'error') if is_js: return 'ok' else: return flask.redirect(flask.url_for( 'view_issue', username=username, repo=repo.name, namespace=repo.namespace, issueid=issueid)) comment = form.comment.data depends = [] for depend in form.depends.data.split(','): if depend.strip(): try: depends.append(int(depend.strip())) except ValueError: pass blocks = [] for block in form.blocks.data.split(','): if block.strip(): try: blocks.append(int(block.strip())) except ValueError: pass assignee = form.assignee.data.strip() or None new_status = form.status.data.strip() or None close_status = form.close_status.data or None if new_status != 'Closed': close_status = None if close_status not in repo.close_status: close_status = None new_priority = None try: new_priority = int(form.priority.data) except: pass tags = [ tag.strip() for tag in form.tag.data.split(',') if tag.strip()] new_milestone = None try: if repo.milestones: new_milestone = form.milestone.data.strip() or None except: pass try: messages = set() # New comment if comment: message = pagure.lib.add_issue_comment( SESSION, issue=issue, comment=comment, user=flask.g.fas_user.username, ticketfolder=APP.config['TICKETS_FOLDER'], ) SESSION.commit() if message and not is_js: messages.add(message) # The status field can be updated by both the admin and the # person who opened the ticket. # Update status if repo_admin or flask.g.fas_user.username == issue.user.user: if new_status in status: message = pagure.lib.edit_issue( SESSION, issue=issue, status=new_status, close_status=close_status, private=issue.private, user=flask.g.fas_user.username, ticketfolder=APP.config['TICKETS_FOLDER'], ) SESSION.commit() if message: messages.add(message) # All the other meta-data can be changed only by admins # while other field will be missing for non-admin and thus # reset if we let them if repo_admin: # Adjust (add/remove) tags messages.union(set(pagure.lib.update_tags( SESSION, issue, tags, username=flask.g.fas_user.username, ticketfolder=APP.config['TICKETS_FOLDER'] ))) # The meta-data can be changed by admins and issue creator, # where issue creators can only change status of their issue while # other fields will be missing for non-admin and thus reset if we let them if repo_admin: # Assign or update assignee of the ticket message = pagure.lib.add_issue_assignee( SESSION, issue=issue, assignee=assignee or None, user=flask.g.fas_user.username, ticketfolder=APP.config['TICKETS_FOLDER'], ) SESSION.commit() if message and message != 'Nothing to change': messages.add(message) # Update priority if str(new_priority) in repo.priorities: message = pagure.lib.edit_issue( SESSION, issue=issue, priority=new_priority, private=issue.private, user=flask.g.fas_user.username, ticketfolder=APP.config['TICKETS_FOLDER'], ) SESSION.commit() if message: messages.add(message) # Update milestone and privacy setting message = pagure.lib.edit_issue( SESSION, issue=issue, milestone=new_milestone, private=form.private.data, user=flask.g.fas_user.username, ticketfolder=APP.config['TICKETS_FOLDER'], ) SESSION.commit() if message: messages.add(message) # Update the custom keys/fields for key in repo.issue_keys: value = flask.request.form.get(key.name) if value: if key.key_type == 'link': links = value.split(',') for link in links: link = link.replace(' ', '') if not urlpattern.match(link): flask.abort( 400, 'Meta-data "link" field ' '(%s) has invalid url (%s) ' % (key.name, link)) messages.add( pagure.lib.set_custom_key_value( SESSION, issue, key, value) ) # Update ticket this one depends on messages.union(set(pagure.lib.update_dependency_issue( SESSION, repo, issue, depends, username=flask.g.fas_user.username, ticketfolder=APP.config['TICKETS_FOLDER'], ))) # Update ticket(s) depending on this one messages.union(set(pagure.lib.update_blocked_issue( SESSION, repo, issue, blocks, username=flask.g.fas_user.username, ticketfolder=APP.config['TICKETS_FOLDER'], ))) if not is_js: for message in messages: flask.flash(message) except pagure.exceptions.PagureException as err: is_js = False SESSION.rollback() flask.flash(err.message, 'error') except SQLAlchemyError as err: # pragma: no cover is_js = False SESSION.rollback() APP.logger.exception(err) flask.flash(str(err), 'error') except filelock.Timeout as err: # pragma: no cover is_js = False SESSION.rollback() APP.logger.exception(err) flask.flash( 'We could not save all the info, please try again', 'error') else: if is_js: return 'notok: %s' % form.errors if is_js: return 'ok' else: return flask.redirect(flask.url_for( 'view_issue', repo=repo.name, username=username, namespace=namespace, issueid=issueid) )
def new_issue(repo, username=None): """ Create a new issue """ repo = pagure.lib.get_project(SESSION, repo, user=username) if repo is None: flask.abort(404, 'Project not found') if not repo.settings.get('issue_tracker', True): flask.abort(404, 'No issue tracker found for this project') status = pagure.lib.get_issue_statuses(SESSION) form = pagure.forms.IssueForm(status=status) if form.validate_on_submit(): title = form.title.data content = form.issue_content.data private = form.private.data try: issue = pagure.lib.new_issue( SESSION, repo=repo, title=title, content=content, private=private or False, user=flask.g.fas_user.username, ticketfolder=APP.config['TICKETS_FOLDER'], ) SESSION.commit() # If there is a file attached, attach it. filestream = flask.request.files.get('filestream') if filestream and '<!!image>' in issue.content: new_filename = pagure.lib.git.add_file_to_git( repo=repo, issue=issue, ticketfolder=APP.config['TICKETS_FOLDER'], user=flask.g.fas_user, filename=filestream.filename, filestream=filestream.stream, ) # Replace the <!!image> tag in the comment with the link # to the actual image filelocation = flask.url_for( 'view_issue_raw_file', repo=repo.name, username=username, filename=new_filename, ) new_filename = new_filename.split('-', 1)[1] url = '[![%s](%s)](%s)' % ( new_filename, filelocation, filelocation) issue.content = issue.content.replace('<!!image>', url) SESSION.add(issue) SESSION.commit() flask.flash('Issue created') return flask.redirect(flask.url_for( '.view_issue', username=username, repo=repo.name, issueid=issue.id)) except pagure.exceptions.PagureException as err: flask.flash(str(err), 'error') except SQLAlchemyError as err: # pragma: no cover SESSION.rollback() flask.flash(str(err), 'error') return flask.render_template( 'new_issue.html', select='issues', form=form, repo=repo, username=username, repo_admin=is_repo_admin(repo), )
def edit_issue(repo, issueid, username=None): """ Edit the specified issue """ repo = pagure.lib.get_project(SESSION, repo, user=username) if repo is None: flask.abort(404, 'Project not found') if not repo.settings.get('issue_tracker', True): flask.abort(404, 'No issue tracker found for this project') issue = pagure.lib.search_issues(SESSION, repo, issueid=issueid) if issue is None or issue.project != repo: flask.abort(404, 'Issue not found') if not (is_repo_admin(repo) or flask.g.fas_user.username == issue.user.username): flask.abort( 403, 'You are not allowed to edit issues for this project') status = pagure.lib.get_issue_statuses(SESSION) form = pagure.forms.IssueForm(status=status) if form.validate_on_submit(): title = form.title.data content = form.issue_content.data status = form.status.data private = form.private.data try: message = pagure.lib.edit_issue( SESSION, issue=issue, title=title, content=content, status=status, user=flask.g.fas_user.username, ticketfolder=APP.config['TICKETS_FOLDER'], private=private, ) SESSION.commit() # If there is a file attached, attach it. filestream = flask.request.files.get('filestream') if filestream and '<!!image>' in issue.content: new_filename = pagure.lib.git.add_file_to_git( repo=repo, issue=issue, ticketfolder=APP.config['TICKETS_FOLDER'], user=flask.g.fas_user, filename=filestream.filename, filestream=filestream.stream, ) # Replace the <!!image> tag in the comment with the link # to the actual image filelocation = flask.url_for( 'view_issue_raw_file', repo=repo.name, username=username, filename=new_filename, ) new_filename = new_filename.split('-', 1)[1] url = '[![%s](%s)](%s)' % ( new_filename, filelocation, filelocation) issue.content = issue.content.replace('<!!image>', url) SESSION.add(issue) SESSION.commit() flask.flash(message) url = flask.url_for( 'view_issue', username=username, repo=repo.name, issueid=issueid) return flask.redirect(url) except pagure.exceptions.PagureException as err: flask.flash(str(err), 'error') except SQLAlchemyError as err: # pragma: no cover SESSION.rollback() flask.flash(str(err), 'error') elif flask.request.method == 'GET': form.title.data = issue.title form.issue_content.data = issue.content form.status.data = issue.status form.private.data = issue.private return flask.render_template( 'new_issue.html', select='issues', type='edit', form=form, repo=repo, username=username, issue=issue, issueid=issueid, repo_admin=is_repo_admin(repo), )
def new_issue(repo, username=None): """ Create a new issue """ repo = pagure.lib.get_project(SESSION, repo, user=username) if repo is None: flask.abort(404, 'Project not found') if not repo.settings.get('issue_tracker', True): flask.abort(404, 'No issue tracker found for this project') form = pagure.forms.IssueFormSimplied() if form.validate_on_submit(): title = form.title.data content = form.issue_content.data private = form.private.data try: issue = pagure.lib.new_issue( SESSION, repo=repo, title=title, content=content, private=private or False, user=flask.g.fas_user.username, ticketfolder=APP.config['TICKETS_FOLDER'], ) SESSION.commit() # If there is a file attached, attach it. filestream = flask.request.files.get('filestream') if filestream and '<!!image>' in issue.content: new_filename = pagure.lib.git.add_file_to_git( repo=repo, issue=issue, ticketfolder=APP.config['TICKETS_FOLDER'], user=flask.g.fas_user, filename=filestream.filename, filestream=filestream.stream, ) # Replace the <!!image> tag in the comment with the link # to the actual image filelocation = flask.url_for( 'view_issue_raw_file', repo=repo.name, username=username, filename=new_filename, ) new_filename = new_filename.split('-', 1)[1] url = '[![%s](%s)](%s)' % ( new_filename, filelocation, filelocation) issue.content = issue.content.replace('<!!image>', url) SESSION.add(issue) SESSION.commit() return flask.redirect(flask.url_for( '.view_issue', username=username, repo=repo.name, issueid=issue.id)) except pagure.exceptions.PagureException as err: flask.flash(str(err), 'error') except SQLAlchemyError as err: # pragma: no cover SESSION.rollback() flask.flash(str(err), 'error') types = None default = None ticketrepopath = os.path.join(APP.config['TICKETS_FOLDER'], repo.path) if os.path.exists(ticketrepopath): ticketrepo = pygit2.Repository(ticketrepopath) if not ticketrepo.is_empty and not ticketrepo.head_is_unborn: commit = ticketrepo[ticketrepo.head.target] # Get the different ticket types files = __get_file_in_tree( ticketrepo, commit.tree, ['templates'], bail_on_tree=True) if files: types = [f.name.rstrip('.md') for f in files] # Get the default template default_file = __get_file_in_tree( ticketrepo, commit.tree, ['templates', 'default.md'], bail_on_tree=True) if default_file: default, _ = pagure.doc_utils.convert_readme( default_file.data, 'md') return flask.render_template( 'new_issue.html', select='issues', form=form, repo=repo, username=username, repo_admin=is_repo_admin(repo), types=types, default=default, )
def view_plugin(repo, plugin, username=None, namespace=None, full=True): """ Presents the settings of the project. """ repo = flask.g.repo if not flask.g.repo_admin: flask.abort( 403, 'You are not allowed to change the settings for this project') # Private repos are not allowed to leak information outside so disabling CI # enables us to keep the repos totally discreate and prevents from leaking # information outside if repo.private and plugin == 'Pagure CI': flask.abort(404, 'Plugin disabled') if plugin in APP.config.get('DISABLED_PLUGINS', []): flask.abort(404, 'Plugin disabled') if plugin == 'default': flask.abort(403, 'This plugin cannot be changed') plugin = pagure.lib.plugins.get_plugin(plugin) fields = [] new = True dbobj = plugin.db_object() if hasattr(repo, plugin.backref): dbobj = getattr(repo, plugin.backref) # There should always be only one, but let's double check if dbobj: new = False else: dbobj = plugin.db_object() form = plugin.form(obj=dbobj) for field in plugin.form_fields: fields.append(getattr(form, field)) if form.validate_on_submit(): form.populate_obj(obj=dbobj) if new: dbobj.project_id = repo.id SESSION.add(dbobj) try: SESSION.flush() except SQLAlchemyError as err: # pragma: no cover SESSION.rollback() _log.exception('Could not add plugin %s', plugin.name) flask.flash('Could not add plugin %s, please contact an admin' % plugin.name) return flask.render_template('plugin.html', select='settings', full=full, repo=repo, username=username, namespace=namespace, plugin=plugin, form=form, fields=fields) if form.active.data: # Set up the main script if necessary plugin.set_up(repo) # Install the plugin itself try: plugin.install(repo, dbobj) flask.flash('Hook %s activated' % plugin.name) except FileNotFoundException as err: _log.exception(err) flask.abort(404, 'No git repo found') else: try: plugin.remove(repo) flask.flash('Hook %s deactivated' % plugin.name) except FileNotFoundException as err: _log.exception(err) flask.abort(404, 'No git repo found') SESSION.commit() return flask.redirect( flask.url_for('view_settings', repo=repo.name, username=username, namespace=namespace)) return flask.render_template('plugin.html', select='settings', full=full, repo=repo, namespace=namespace, username=username, plugin=plugin, form=form, fields=fields)
def api_new_issue(repo, username=None): """ Create a new issue ------------------ Open a new issue on a project. :: POST /api/0/<repo>/new_issue :: POST /api/0/fork/<username>/<repo>/new_issue Input ^^^^^ +--------------+----------+--------------+-----------------------------+ | Key | Type | Optionality | Description | +==============+==========+==============+=============================+ | ``title`` | string | Mandatory | The title of the issue | +--------------+----------+--------------+-----------------------------+ | ``content`` | string | Mandatory | | The description of the | | | | | issue | +--------------+----------+--------------+-----------------------------+ | ``private`` | boolean | Optional | | Include this key if | | | | | you want a private issue | | | | | to be created | +--------------+----------+--------------+-----------------------------+ Sample response ^^^^^^^^^^^^^^^ :: { "message": "Issue created" } """ repo = pagure.lib.get_project(SESSION, repo, user=username) output = {} if repo is None: raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOPROJECT) if not repo.settings.get('issue_tracker', True): raise pagure.exceptions.APIError(404, error_code=APIERROR.ETRACKERDISABLED) if repo != flask.g.token.project: raise pagure.exceptions.APIError(401, error_code=APIERROR.EINVALIDTOK) form = pagure.forms.IssueFormSimplied(csrf_enabled=False) if form.validate_on_submit(): title = form.title.data content = form.issue_content.data private = form.private.data try: issue = pagure.lib.new_issue( SESSION, repo=repo, title=title, content=content, private=private or False, user=flask.g.fas_user.username, ticketfolder=APP.config['TICKETS_FOLDER'], ) SESSION.flush() # If there is a file attached, attach it. filestream = flask.request.files.get('filestream') if filestream and '<!!image>' in issue.content: new_filename = pagure.lib.git.add_file_to_git( repo=repo, issue=issue, ticketfolder=APP.config['TICKETS_FOLDER'], user=flask.g.fas_user, filename=filestream.filename, filestream=filestream.stream, ) # Replace the <!!image> tag in the comment with the link # to the actual image filelocation = flask.url_for( 'view_issue_raw_file', repo=repo.name, username=username, filename=new_filename, ) new_filename = new_filename.split('-', 1)[1] url = '[![%s](%s)](%s)' % (new_filename, filelocation, filelocation) issue.content = issue.content.replace('<!!image>', url) SESSION.add(issue) SESSION.flush() SESSION.commit() output['message'] = 'Issue created' except SQLAlchemyError, err: # pragma: no cover SESSION.rollback() APP.logger.exception(err) raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR)
def new_issue(repo, username=None): """ Create a new issue """ repo = pagure.lib.get_project(SESSION, repo, user=username) if repo is None: flask.abort(404, 'Project not found') if not repo.settings.get('issue_tracker', True): flask.abort(404, 'No issue tracker found for this project') status = pagure.lib.get_issue_statuses(SESSION) form = pagure.forms.IssueForm(status=status) if form.validate_on_submit(): title = form.title.data content = form.issue_content.data private = form.private.data try: issue = pagure.lib.new_issue( SESSION, repo=repo, title=title, content=content, private=private or False, user=flask.g.fas_user.username, ticketfolder=APP.config['TICKETS_FOLDER'], ) SESSION.commit() # If there is a file attached, attach it. filestream = flask.request.files.get('filestream') if filestream and '<!!image>' in issue.content: new_filename = pagure.lib.git.add_file_to_git( repo=repo, issue=issue, ticketfolder=APP.config['TICKETS_FOLDER'], user=flask.g.fas_user, filename=filestream.filename, filestream=filestream.stream, ) # Replace the <!!image> tag in the comment with the link # to the actual image filelocation = flask.url_for( 'view_issue_raw_file', repo=repo.name, username=username, filename=new_filename, ) new_filename = new_filename.split('-', 1)[1] url = '[![%s](%s)](%s)' % ( new_filename, filelocation, filelocation) issue.content = issue.content.replace('<!!image>', url) SESSION.add(issue) SESSION.commit() flask.flash('Issue created') return flask.redirect(flask.url_for( '.view_issue', username=username, repo=repo.name, issueid=issue.id)) except pagure.exceptions.PagureException, err: flask.flash(str(err), 'error') except SQLAlchemyError, err: # pragma: no cover SESSION.rollback() flask.flash(str(err), 'error')
def api_new_issue(repo, username=None): """ Create a new issue ------------------ This endpoint can be used to open an issue on a project :: /api/0/<repo>/new_issue /api/0/fork/<username>/<repo>/new_issue Accepts POST queries only. :arg title: The title of the issue/ticket to create :arg content: The content of the issue to create (ie the description of the problem) :arg private: A boolean specifying whether this issue is private or not Sample response: :: { "message": "Issue created" } """ repo = pagure.lib.get_project(SESSION, repo, user=username) output = {} if repo is None: raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOPROJECT) if not repo.settings.get('issue_tracker', True): raise pagure.exceptions.APIError( 404, error_code=APIERROR.ETRACKERDISABLED) if repo != flask.g.token.project: raise pagure.exceptions.APIError(401, error_code=APIERROR.EINVALIDTOK) status = pagure.lib.get_issue_statuses(SESSION) form = pagure.forms.IssueForm(status=status, csrf_enabled=False) if form.validate_on_submit(): title = form.title.data content = form.issue_content.data private = form.private.data try: issue = pagure.lib.new_issue( SESSION, repo=repo, title=title, content=content, private=private or False, user=flask.g.fas_user.username, ticketfolder=APP.config['TICKETS_FOLDER'], ) SESSION.flush() # If there is a file attached, attach it. filestream = flask.request.files.get('filestream') if filestream and '<!!image>' in issue.content: new_filename = pagure.lib.git.add_file_to_git( repo=repo, issue=issue, ticketfolder=APP.config['TICKETS_FOLDER'], user=flask.g.fas_user, filename=filestream.filename, filestream=filestream.stream, ) # Replace the <!!image> tag in the comment with the link # to the actual image filelocation = flask.url_for( 'view_issue_raw_file', repo=repo.name, username=username, filename=new_filename, ) new_filename = new_filename.split('-', 1)[1] url = '[![%s](%s)](%s)' % ( new_filename, filelocation, filelocation) issue.content = issue.content.replace('<!!image>', url) SESSION.add(issue) SESSION.flush() SESSION.commit() output['message'] = 'Issue created' except SQLAlchemyError, err: # pragma: no cover SESSION.rollback() APP.logger.exception(err) raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR)
def edit_issue(repo, issueid, username=None, namespace=None): """ Edit the specified issue """ repo = flask.g.repo if not repo.settings.get('issue_tracker', True): flask.abort(404, 'No issue tracker found for this project') issue = pagure.lib.search_issues(SESSION, repo, issueid=issueid) if issue is None or issue.project != repo: flask.abort(404, 'Issue not found') if not (flask.g.repo_admin or flask.g.fas_user.username == issue.user.username): flask.abort( 403, 'You are not allowed to edit issues for this project') status = pagure.lib.get_issue_statuses(SESSION) form = pagure.forms.IssueForm(status=status) if form.validate_on_submit(): title = form.title.data content = form.issue_content.data status = form.status.data private = form.private.data try: user_obj = pagure.lib.get_user( SESSION, flask.g.fas_user.username) except pagure.exceptions.PagureException: flask.abort( 404, 'No such user found in the database: %s' % ( flask.g.fas_user.username)) try: message = pagure.lib.edit_issue( SESSION, issue=issue, title=title, content=content, status=status, user=flask.g.fas_user.username, ticketfolder=APP.config['TICKETS_FOLDER'], private=private, ) SESSION.commit() # If there is a file attached, attach it. filestream = flask.request.files.get('filestream') if filestream and '<!!image>' in issue.content: new_filename = pagure.lib.git.add_file_to_git( repo=repo, issue=issue, ticketfolder=APP.config['TICKETS_FOLDER'], user=user_obj, filename=filestream.filename, filestream=filestream.stream, ) # Replace the <!!image> tag in the comment with the link # to the actual image filelocation = flask.url_for( 'view_issue_raw_file', repo=repo.name, namespace=repo.namespace, username=username, filename=new_filename, ) new_filename = new_filename.split('-', 1)[1] url = '[![%s](%s)](%s)' % ( new_filename, filelocation, filelocation) issue.content = issue.content.replace('<!!image>', url) SESSION.add(issue) SESSION.commit() flask.flash(message) url = flask.url_for( 'view_issue', username=username, namespace=namespace, repo=repo.name, issueid=issueid) return flask.redirect(url) except pagure.exceptions.PagureException as err: flask.flash(str(err), 'error') except SQLAlchemyError as err: # pragma: no cover SESSION.rollback() flask.flash(str(err), 'error') elif flask.request.method == 'GET': form.title.data = issue.title form.issue_content.data = issue.content form.status.data = issue.status form.private.data = issue.private return flask.render_template( 'new_issue.html', select='issues', type='edit', form=form, repo=repo, username=username, issue=issue, issueid=issueid, )
def api_new_issue(repo, username=None): """ Create a new issue ------------------ Open a new issue on a project. :: POST /api/0/<repo>/new_issue :: POST /api/0/fork/<username>/<repo>/new_issue Input ^^^^^ +--------------+----------+--------------+-----------------------------+ | Key | Type | Optionality | Description | +==============+==========+==============+=============================+ | ``title`` | string | Mandatory | The title of the issue | +--------------+----------+--------------+-----------------------------+ | ``content`` | string | Mandatory | | The description of the | | | | | issue | +--------------+----------+--------------+-----------------------------+ | ``private`` | boolean | Optional | | Include this key if | | | | | you want a private issue | | | | | to be created | +--------------+----------+--------------+-----------------------------+ Sample response ^^^^^^^^^^^^^^^ :: { "message": "Issue created" } """ repo = pagure.lib.get_project(SESSION, repo, user=username) output = {} if repo is None: raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOPROJECT) if not repo.settings.get('issue_tracker', True): raise pagure.exceptions.APIError( 404, error_code=APIERROR.ETRACKERDISABLED) if repo != flask.g.token.project: raise pagure.exceptions.APIError(401, error_code=APIERROR.EINVALIDTOK) form = pagure.forms.IssueFormSimplied(csrf_enabled=False) if form.validate_on_submit(): title = form.title.data content = form.issue_content.data private = str(form.private.data).lower() in ['true', '1'] try: issue = pagure.lib.new_issue( SESSION, repo=repo, title=title, content=content, private=private, user=flask.g.fas_user.username, ticketfolder=APP.config['TICKETS_FOLDER'], ) SESSION.flush() # If there is a file attached, attach it. filestream = flask.request.files.get('filestream') if filestream and '<!!image>' in issue.content: new_filename = pagure.lib.git.add_file_to_git( repo=repo, issue=issue, ticketfolder=APP.config['TICKETS_FOLDER'], user=flask.g.fas_user, filename=filestream.filename, filestream=filestream.stream, ) # Replace the <!!image> tag in the comment with the link # to the actual image filelocation = flask.url_for( 'view_issue_raw_file', repo=repo.name, username=username, filename=new_filename, ) new_filename = new_filename.split('-', 1)[1] url = '[![%s](%s)](%s)' % ( new_filename, filelocation, filelocation) issue.content = issue.content.replace('<!!image>', url) SESSION.add(issue) SESSION.flush() SESSION.commit() output['message'] = 'Issue created' except SQLAlchemyError as err: # pragma: no cover SESSION.rollback() APP.logger.exception(err) raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR) else: raise pagure.exceptions.APIError(400, error_code=APIERROR.EINVALIDREQ) jsonout = flask.jsonify(output) return jsonout
def view_plugin(repo, plugin, username=None, full=True): """ Presents the settings of the project. """ repo = pagure.lib.get_project(SESSION, repo, user=username) if not repo: flask.abort(404, 'Project not found') if not is_repo_admin(repo): flask.abort( 403, 'You are not allowed to change the settings for this project') if plugin in APP.config.get('DISABLED_PLUGINS', []): flask.abort(404, 'Plugin disabled') plugin = get_plugin(plugin) fields = [] new = True dbobj = plugin.db_object() if hasattr(repo, plugin.backref): dbobj = getattr(repo, plugin.backref) # There should always be only one, but let's double check if dbobj and len(dbobj) > 0: dbobj = dbobj[0] new = False else: dbobj = plugin.db_object() form = plugin.form(obj=dbobj) for field in plugin.form_fields: fields.append(getattr(form, field)) if form.validate_on_submit(): form.populate_obj(obj=dbobj) if new: dbobj.project_id = repo.id SESSION.add(dbobj) try: SESSION.flush() except SQLAlchemyError as err: # pragma: no cover SESSION.rollback() APP.logger.debug('Could not add plugin %s', plugin.name) APP.logger.exception(err) flask.flash( 'Could not add plugin %s, please contact an admin' % plugin.name) return flask.render_template( 'plugin.html', select='settings', full=full, repo=repo, username=username, plugin=plugin, form=form, fields=fields) if form.active.data: # Set up the main script if necessary plugin.set_up(repo) # Install the plugin itself try: plugin.install(repo, dbobj) flask.flash('Hook %s activated' % plugin.name) except FileNotFoundException as err: pagure.APP.logger.exception(err) flask.abort(404, 'No git repo found') else: try: plugin.remove(repo) flask.flash('Hook %s inactived' % plugin.name) except FileNotFoundException as err: pagure.APP.logger.exception(err) flask.abort(404, 'No git repo found') SESSION.commit() return flask.redirect(flask.url_for( 'view_settings', repo=repo.name, username=username)) return flask.render_template( 'plugin.html', select='settings', full=full, repo=repo, username=username, plugin=plugin, form=form, fields=fields)
def do_login(): """ Log in the user. """ form = forms.LoginForm() next_url = flask.request.form.get('next_url') if not next_url or next_url == 'None': next_url = flask.url_for('index') if form.validate_on_submit(): username = form.username.data user_obj = pagure.lib.search_user(SESSION, username=username) if not user_obj: flask.flash('Username or password invalid.', 'error') return flask.redirect(flask.url_for('auth_login')) try: password_checks = check_password(form.password.data, user_obj.password, seed=APP.config.get( 'PASSWORD_SEED', None)) except pagure.exceptions.PagureException as err: _log.exception(err) flask.flash('Username or password of invalid format.', 'error') return flask.redirect(flask.url_for('auth_login')) if not password_checks: flask.flash('Username or password invalid.', 'error') return flask.redirect(flask.url_for('auth_login')) elif user_obj.token: flask.flash( 'Invalid user, did you confirm the creation with the url ' 'provided by email?', 'error') return flask.redirect(flask.url_for('auth_login')) else: if not user_obj.password.startswith('$2$'): user_obj.password = generate_hashed_value(form.password.data) SESSION.add(user_obj) visit_key = pagure.lib.login.id_generator(40) now = datetime.datetime.utcnow() expiry = now + datetime.timedelta(days=30) session = model.PagureUserVisit( user_id=user_obj.id, user_ip=flask.request.remote_addr, visit_key=visit_key, expiry=expiry, ) SESSION.add(session) try: SESSION.commit() flask.g.fas_user = user_obj flask.g.fas_session_id = visit_key flask.g.fas_user.login_time = now flask.flash('Welcome %s' % user_obj.username) except SQLAlchemyError as err: # pragma: no cover flask.flash( 'Could not set the session in the db, ' 'please report this error to an admin', 'error') _log.exception(err) return flask.redirect(next_url) else: flask.flash('Insufficient information provided', 'error') return flask.redirect(flask.url_for('auth_login'))
def view_plugin(repo, plugin, username=None, full=True): """ Presents the settings of the project. """ repo = pagure.lib.get_project(SESSION, repo, user=username) if not repo: flask.abort(404, 'Project not found') if not is_repo_admin(repo): flask.abort( 403, 'You are not allowed to change the settings for this project') if plugin in APP.config.get('DISABLED_PLUGINS', []): flask.abort(404, 'Plugin disabled') plugin = get_plugin(plugin) fields = [] new = True dbobj = plugin.db_object() if hasattr(repo, plugin.backref): dbobj = getattr(repo, plugin.backref) # There should always be only one, but let's double check if dbobj and len(dbobj) > 0: dbobj = dbobj[0] new = False else: dbobj = plugin.db_object() form = plugin.form(obj=dbobj) for field in plugin.form_fields: fields.append(getattr(form, field)) if form.validate_on_submit(): form.populate_obj(obj=dbobj) if new: dbobj.project_id = repo.id SESSION.add(dbobj) try: SESSION.flush() except SQLAlchemyError as err: # pragma: no cover SESSION.rollback() APP.logger.debug('Could not add plugin %s', plugin.name) APP.logger.exception(err) flask.flash('Could not add plugin %s, please contact an admin' % plugin.name) return flask.render_template('plugin.html', select='settings', full=full, repo=repo, username=username, plugin=plugin, form=form, fields=fields) if form.active.data: # Set up the main script if necessary plugin.set_up(repo) # Install the plugin itself plugin.install(repo, dbobj) flask.flash('Hook %s activated' % plugin.name) else: plugin.remove(repo) flask.flash('Hook %s inactived' % plugin.name) SESSION.commit() return flask.redirect( flask.url_for('view_settings', repo=repo.name, username=username)) return flask.render_template('plugin.html', select='settings', full=full, repo=repo, username=username, plugin=plugin, form=form, fields=fields)
def api_new_issue(repo, username=None, namespace=None): """ Create a new issue ------------------ Open a new issue on a project. :: POST /api/0/<repo>/new_issue POST /api/0/<namespace>/<repo>/new_issue :: POST /api/0/fork/<username>/<repo>/new_issue POST /api/0/fork/<username>/<namespace>/<repo>/new_issue Input ^^^^^ +-------------------+--------+-------------+---------------------------+ | Key | Type | Optionality | Description | +===================+========+=============+===========================+ | ``title`` | string | Mandatory | The title of the issue | +-------------------+--------+-------------+---------------------------+ | ``issue_content`` | string | Mandatory | | The description of the | | | | | issue | +-------------------+--------+-------------+---------------------------+ | ``private`` | boolean| Optional | | Include this key if | | | | | you want a private issue| | | | | to be created | +-------------------+--------+-------------+---------------------------+ | ``priority`` | string | Optional | | The priority to set to | | | | | this ticket from the | | | | | list of priorities set | | | | | in the project | +-------------------+--------+-------------+---------------------------+ | ``milestone`` | string | Optional | | The milestone to assign | | | | | to this ticket from the | | | | | list of milestones set | | | | | in the project | +-------------------+--------+-------------+---------------------------+ | ``tag`` | string | Optional | | Comma separated list of | | | | | tags to link to this | | | | | ticket from the list of | | | | | tags in the project | +-------------------+--------+-------------+---------------------------+ | ``assignee`` | string | Optional | | The username of the user| | | | | to assign this ticket to| +-------------------+--------+-------------+---------------------------+ Sample response ^^^^^^^^^^^^^^^ :: { "issue": { "assignee": null, "blocks": [], "close_status": null, "closed_at": null, "comments": [], "content": "This issue needs attention", "custom_fields": [], "date_created": "1479458613", "depends": [], "id": 1, "milestone": null, "priority": null, "private": false, "status": "Open", "tags": [], "title": "test issue", "user": { "fullname": "PY C", "name": "pingou" } }, "message": "Issue created" } """ output = {} repo = _get_repo(repo, username, namespace) _check_issue_tracker(repo) _check_token(repo, project_token=False) user_obj = pagure.lib.get_user(SESSION, flask.g.fas_user.username) if not user_obj: raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOUSER) form = pagure.forms.IssueFormSimplied(priorities=repo.priorities, milestones=repo.milestones, csrf_enabled=False) if form.validate_on_submit(): title = form.title.data content = form.issue_content.data milestone = form.milestone.data or None private = str(form.private.data).lower() in ['true', '1'] priority = form.priority.data or None assignee = flask.request.form.get('assignee', '').strip() or None tags = [ tag.strip() for tag in flask.request.form.get('tag', '').split(',') if tag.strip() ] try: issue = pagure.lib.new_issue( SESSION, repo=repo, title=title, content=content, private=private, assignee=assignee, milestone=milestone, priority=priority, tags=tags, user=flask.g.fas_user.username, ticketfolder=APP.config['TICKETS_FOLDER'], ) SESSION.flush() # If there is a file attached, attach it. filestream = flask.request.files.get('filestream') if filestream and '<!!image>' in issue.content: new_filename = pagure.lib.add_attachment( repo=repo, issue=issue, attachmentfolder=APP.config['ATTACHMENTS_FOLDER'], user=user_obj, filename=filestream.filename, filestream=filestream.stream, ) # Replace the <!!image> tag in the comment with the link # to the actual image filelocation = flask.url_for( 'view_issue_raw_file', repo=repo.name, username=username, filename=new_filename, ) new_filename = new_filename.split('-', 1)[1] url = '[![%s](%s)](%s)' % (new_filename, filelocation, filelocation) issue.content = issue.content.replace('<!!image>', url) SESSION.add(issue) SESSION.flush() SESSION.commit() output['message'] = 'Issue created' output['issue'] = issue.to_json(public=True) except SQLAlchemyError as err: # pragma: no cover SESSION.rollback() APP.logger.exception(err) raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR) else: raise pagure.exceptions.APIError(400, error_code=APIERROR.EINVALIDREQ, errors=form.errors) jsonout = flask.jsonify(output) return jsonout