Ejemplo n.º 1
0
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()
Ejemplo n.º 2
0
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))
Ejemplo n.º 3
0
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')
Ejemplo n.º 4
0
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'))
Ejemplo n.º 5
0
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))
Ejemplo n.º 6
0
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))
Ejemplo n.º 7
0
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))