def user(username, email, password, role, active): """Create a new application user.""" from tracker import db from tracker.user import hash_password from tracker.user import random_string user_by_name = db.get(User, name=username) if user_by_name: echo('Error: username already exists', err=True) exit(1) user_by_email = db.get(User, email=email) if user_by_email: echo('Error: e-mail already exists', err=True) exit(1) user = User() user.name = username user.email = email user.salt = random_string() user.password = hash_password(password, user.salt) user.role = UserRole.fromstring(role) user.active = active db.session.add(user) db.session.commit()
def edit_advisory(advisory_id): advisory = db.get(Advisory, id=advisory_id) if not advisory: return not_found() form = AdvisoryEditForm(advisory.id) if not form.is_submitted(): form.workaround.data = advisory.workaround form.impact.data = advisory.impact form.reference.data = advisory.reference if not advisory.reference and Publication.published == advisory.publication: form.reference.data = advisory_fetch_reference_url_from_mailman(advisory) if not form.validate_on_submit(): if advisory.reference: flash(WARNING_ADVISORY_ALREADY_PUBLISHED, 'warning') return render_template('form/advisory.html', title='Edit {}'.format(advisory.id), Advisory=Advisory, form=form) advisory.impact = form.impact.data or None advisory.workaround = form.workaround.data or None if advisory.reference != form.reference.data: advisory.content = form.advisory_content advisory_extend_model_from_advisory_text(advisory) advisory.reference = form.reference.data or None db.session.commit() flash('Edited {}'.format(advisory.id)) return redirect('/{}'.format(advisory.id))
def copy_issue(issue): cve = db.get(CVE, id=issue) if not cve: return not_found() form = CVEForm() form.cve.data = cve.id form.description.data = cve.description form.issue_type.data = cve.issue_type form.notes.data = cve.notes form.reference.data = cve.reference form.remote.data = cve.remote.name form.severity.data = cve.severity.name return render_template('form/cve.html', title='Add CVE', form=form, CVE=CVE, action='/cve/add')
def sso_auth(): try: token = oauth.idp.authorize_access_token() parsed_token = oauth.idp.parse_id_token(token) except AuthlibBaseError as e: return bad_request(f'{e.description}') idp_user_sub = parsed_token.get('sub') if not idp_user_sub: return bad_request(LOGIN_ERROR_MISSING_USER_SUB_FROM_TOKEN) idp_email_verified = parsed_token.get('email_verified') if not idp_email_verified: return forbidden(LOGIN_ERROR_EMAIL_ADDRESS_NOT_VERIFIED) idp_email = parsed_token.get('email') if not idp_email: return bad_request(LOGIN_ERROR_MISSING_EMAIL_FROM_TOKEN) idp_username = parsed_token.get('preferred_username') if not idp_username: return bad_request(LOGIN_ERROR_MISSING_USERNAME_FROM_TOKEN) idp_groups = parsed_token.get('groups') if idp_groups is None: return bad_request(LOGIN_ERROR_MISSING_GROUPS_FROM_TOKEN) user_role = get_user_role_from_idp_groups(idp_groups) if not user_role: return forbidden(LOGIN_ERROR_PERMISSION_DENIED) # get local user from current authenticated idp id user = db.get(User, idp_id=idp_user_sub) if not user: # get local user from idp email address user = db.get(User, email=idp_email) if user: # prevent impersonation by checking whether this email is associated with an idp id if user.idp_id: return forbidden( LOGIN_ERROR_EMAIL_ASSOCIATED_WITH_DIFFERENT_SUB) # email is already associated with a different username if user.name != idp_username: return forbidden( LOGIN_ERROR_EMAIL_ASSOCIATED_WITH_DIFFERENT_USERNAME) # prevent integrity error for mismatching mail between db and keycloak check_user = db.get(User, name=idp_username) if check_user and check_user.email != idp_email: return forbidden( LOGIN_ERROR_USERNAME_ASSOCIATE_WITH_DIFFERENT_EMAIL) if user: user.role = user_role user.email = idp_email else: salt = random_string() user = db.create(User, name=idp_username, email=idp_email, salt=salt, password=hash_password( random_string(TRACKER_PASSWORD_LENGTH_MAX), salt), role=user_role, active=True, idp_id=idp_user_sub) db.session.add(user) db.session.commit() user = user_assign_new_token(user) user.is_authenticated = True login_user(user) return redirect(url_for('tracker.index'))
def add_cve(): form = CVEForm() if not form.validate_on_submit(): return render_template('form/cve.html', title='Add CVE', form=form, CVE=CVE) cve = db.get(CVE, id=form.cve.data) if cve is not None: advisories = (db.session.query(Advisory).join( CVEGroupEntry, CVEGroupEntry.cve_id == cve.id).join( CVEGroup, CVEGroupEntry.group).join( CVEGroupPackage, CVEGroup.packages).filter( Advisory.group_package_id == CVEGroupPackage.id) ).all() if not user_can_edit_issue(advisories): flash(ERROR_ISSUE_REFERENCED_BY_ADVISORY.format(cve.id), 'error') return forbidden() not_merged = [] merged = False # try to merge issue_type if 'unknown' != form.issue_type.data: if 'unknown' == cve.issue_type: cve.issue_type = form.issue_type.data merged = True elif form.issue_type.data != cve.issue_type: not_merged.append(form.issue_type) form.issue_type.data = cve.issue_type # try to merge severity form_severity = Severity.fromstring(form.severity.data) if Severity.unknown != form_severity: if Severity.unknown == cve.severity: cve.severity = form_severity merged = True elif form_severity != cve.severity: not_merged.append(form.severity) form.severity.data = cve.severity.name # try to merge remote form_remote = Remote.fromstring(form.remote.data) if Remote.unknown != form_remote: if Remote.unknown == cve.remote: cve.remote = form_remote merged = True elif form_remote != cve.remote: not_merged.append(form.remote) form.remote.data = cve.remote.name # try to merge description if form.description.data: if not cve.description: cve.description = form.description.data merged = True elif form.description.data != cve.description: not_merged.append(form.description) form.description.data = cve.description # try to merge references references = cve.reference.splitlines() if cve.reference else [] old_references = references.copy() form_references = form.reference.data.splitlines( ) if form.reference.data else [] for reference in form_references: if reference not in references: references.append(reference) merged = True if old_references != references: cve.reference = '\n'.join(references) form.reference.data = cve.reference # try to merge notes if form.notes.data: if not cve.notes: cve.notes = form.notes.data merged = True elif form.notes.data != cve.notes: not_merged.append(form.notes) form.notes.data = cve.notes # if something got merged, commit and flash if merged: db.session.commit() flash(CVE_MERGED.format(cve.id)) # warn if something failed to be merged if not_merged: for field in not_merged: field.errors.append(ERROR_UNMERGEABLE) not_merged_labels = [field.label.text for field in not_merged] flash( CVE_MERGED_PARTIALLY.format(cve.id, ', '.join(not_merged_labels)), 'warning') return render_template('form/cve.html', title='Edit {}'.format(cve), form=form, CVE=CVE, action='{}/edit'.format(cve.id)) return redirect('/{}'.format(cve.id)) cve = CVE() cve.id = form.cve.data cve.issue_type = form.issue_type.data cve.description = form.description.data cve.severity = Severity.fromstring(form.severity.data) cve.remote = Remote.fromstring(form.remote.data) cve.reference = form.reference.data cve.notes = form.notes.data db.session.add(cve) db.session.commit() flash('Added {}'.format(cve.id)) return redirect('/{}'.format(cve.id))
def edit_group(avg): group_id = avg.replace('AVG-', '') group_data = (db.session.query(CVEGroup, CVE, func.group_concat(CVEGroupPackage.pkgname, ' '), Advisory) .filter(CVEGroup.id == group_id) .join(CVEGroupEntry, CVEGroup.issues) .join(CVE, CVEGroupEntry.cve) .join(CVEGroupPackage, CVEGroup.packages) .outerjoin(Advisory, Advisory.group_package_id == CVEGroupPackage.id) .group_by(CVEGroup.id).group_by(CVE.id).group_by(CVEGroupPackage.pkgname) .order_by(CVE.id)).all() if not group_data: return not_found() group = group_data[0][0] issues = set([cve for (group, cve, pkg, advisory) in group_data]) issue_ids = set([cve.id for cve in issues]) pkgnames = set(chain.from_iterable([pkg.split(' ') for (group, cve, pkg, advisory) in group_data])) advisories = set(advisory for (group, cve, pkg, advisory) in group_data if advisory) if not user_can_edit_group(advisories): return forbidden() form = GroupForm(pkgnames) if not form.is_submitted(): form.affected.data = group.affected form.fixed.data = group.fixed form.pkgnames.data = "\n".join(sorted(pkgnames)) form.status.data = status_to_affected(group.status).name form.reference.data = group.reference form.notes.data = group.notes form.bug_ticket.data = group.bug_ticket form.advisory_qualified.data = group.advisory_qualified and group.status is not Status.not_affected issue_ids = sorted(issue_ids, key=issue_to_numeric) form.cve.data = "\n".join(issue_ids) if not form.validate_on_submit(): if advisories: flash('WARNING: This is referenced by an already published advisory!', 'warning') return render_template('form/group.html', title='Edit {}'.format(avg), form=form, CVEGroup=CVEGroup) pkgnames_edited = multiline_to_list(form.pkgnames.data) group.affected = form.affected.data group.fixed = form.fixed.data group.status = affected_to_status(Affected.fromstring(form.status.data), pkgnames_edited[0], group.fixed) group.bug_ticket = form.bug_ticket.data group.reference = form.reference.data group.notes = form.notes.data group.advisory_qualified = form.advisory_qualified.data and group.status is not Status.not_affected cve_ids = multiline_to_list(form.cve.data) cve_ids = set(filter(lambda s: s.startswith('CVE-'), cve_ids)) issues_removed = set(filter(lambda issue: issue not in cve_ids, issue_ids)) issues_added = set(filter(lambda issue: issue not in issue_ids, cve_ids)) issues_final = set(filter(lambda issue: issue.id not in issues_removed, issues)) issues_changed = any(issues_added) or any(issues_removed) # remove old issues for issue in filter(lambda issue: issue.cve_id in issues_removed, list(group.issues)): group.issues.remove(issue) flash('Removed {}'.format(issue.cve_id)) # add new issues severities = [issue.severity for issue in list(filter(lambda issue: issue.id not in issues_removed, issues))] for cve_id in issues_added: # TODO check if we can avoid this by the latter append call cve = db.get(CVE, id=cve_id) if not cve: cve = CVE.new(id=cve_id) db.get_or_create(CVEGroupEntry, group=group, cve=cve) flash('Added {}'.format(cve.id)) severities.append(cve.severity) issues_final.add(cve) group.severity = highest_severity(severities) pkgnames_removed = set(filter(lambda pkgname: pkgname not in pkgnames_edited, pkgnames)) pkgnames_added = set(filter(lambda pkgname: pkgname not in pkgnames, pkgnames_edited)) pkgnames_changed = any(pkgnames_removed) or any(pkgnames_added) # remove old packages for pkg in filter(lambda pkg: pkg.pkgname in pkgnames_removed, list(group.packages)): group.packages.remove(pkg) flash('Removed {}'.format(pkg.pkgname)) # add new packages for pkgname in pkgnames_added: db.get_or_create(CVEGroupPackage, pkgname=pkgname, group=group) flash('Added {}'.format(pkgname)) # update scheduled advisories for advisory in advisories: if Publication.published == advisory.publication: continue issue_type = 'multiple issues' if len(set([issue.issue_type for issue in issues_final])) > 1 else next(iter(issues_final)).issue_type advisory.advisory_type = issue_type # update changed date on modification if pkgnames_changed or issues_changed or db.session.is_modified(group): group.changed = datetime.utcnow() flash('Edited {}'.format(group.name)) db.session.commit() return redirect('/{}'.format(group.name))
def edit_advisory(advisory_id): advisory: Advisory = db.get(Advisory, id=advisory_id) if not advisory: return not_found() form = AdvisoryEditForm(advisory.id) if not form.is_submitted(): form.workaround.data = advisory.workaround form.impact.data = advisory.impact form.reference.data = advisory.reference if not advisory.reference and Publication.published == advisory.publication: form.reference.data = advisory_fetch_reference_url_from_mailman( advisory) form.changed.data = str(advisory.changed) form.changed_latest.data = str(advisory.changed) concurrent_modification = str(advisory.changed) != form.changed.data if not form.validate_on_submit() or ( concurrent_modification and not (form.force_submit.data and str(advisory.changed) == form.changed_latest.data)): if advisory.reference: flash(WARNING_ADVISORY_ALREADY_PUBLISHED, 'warning') code = 200 advisory_new = None if concurrent_modification: flash('WARNING: The remote data has changed!', 'warning') code = Conflict.code advisory_new = Advisory() advisory_new.id = form.advisory_id advisory_new.workaround = form.workaround.data or None advisory_new.workaround_mod = advisory.workaround != advisory_new.workaround advisory_new.impact = form.impact.data or None advisory_new.impact_mod = advisory.impact != advisory_new.impact advisory_new.reference = form.reference.data or None advisory_new.reference_mod = advisory.reference != advisory_new.reference if form.changed_latest.data != advisory.changed: form.force_submit.data = False form.changed_latest.data = str(advisory.changed) Transaction = versioning_manager.transaction_cls VersionClassAdvisory = version_class(Advisory) version = (db.session.query( Transaction, VersionClassAdvisory).outerjoin( VersionClassAdvisory, Transaction.id == VersionClassAdvisory.transaction_id).filter( VersionClassAdvisory.id == advisory.id).order_by( Transaction.issued_at.desc())).first()[1] advisory_new.transaction = version.transaction advisory_new.operation_type = version.operation_type advisory_new.previous = advisory return render_template( 'form/advisory.html', title='Edit {}'.format(advisory.id), Advisory=Advisory, advisory=advisory_new, form=form, concurrent_modification=concurrent_modification, can_watch_user_log=user_can_watch_user_log()), code advisory.impact = form.impact.data or None advisory.workaround = form.workaround.data or None if advisory.reference != form.reference.data: advisory.content = form.advisory_content advisory_extend_model_from_advisory_text(advisory) advisory.reference = form.reference.data or None # update changed date on modification if db.session.is_modified(advisory): advisory.changed = datetime.utcnow() flash('Edited {}'.format(advisory.id)) db.session.commit() return redirect('/{}'.format(advisory.id))