def route_issue_delete(component_id, component_issue_id): # get firmware component issue = db.session.query(ComponentIssue).\ filter(Component.component_id == component_id, ComponentIssue.component_issue_id == component_issue_id).first() if not issue: flash('No issue matched!', 'danger') return redirect( url_for('components.route_show', component_id=component_id)) # permission check md = issue.md if not md.check_acl('@modify-updateinfo'): flash('Permission denied: Unable to modify firmware', 'danger') return redirect( url_for('components.route_show', component_id=component_id)) # remove issue db.session.delete(issue) md.fw.mark_dirty() md.fw.signed_timestamp = None db.session.commit() _async_sign_fw.apply_async(args=(md.fw.firmware_id, ), queue='firmware') # log flash('Removed {}'.format(issue.value), 'info') return redirect( url_for('components.route_show', component_id=md.component_id, page='issues'))
def route_keyword_create(component_id): """ Adds one or more keywords to the existing component """ # check we have data for key in ['value']: if key not in request.form or not request.form[key]: return _error_internal('No %s specified!' % key) # get firmware component md = db.session.query(Component).\ filter(Component.component_id == component_id).first() if not md: flash('No component matched!', 'danger') return redirect(url_for('firmware.route_firmware')) # security check if not md.check_acl('@modify-keywords'): flash('Permission denied: Unable to modify other vendor firmware', 'danger') return redirect( url_for('components.route_show', component_id=component_id)) # add keyword md.add_keywords_from_string(request.form['value']) md.fw.mark_dirty() md.fw.signed_timestamp = None db.session.commit() _async_sign_fw.apply_async(args=(md.fw.firmware_id, ), queue='firmware') flash('Added keywords', 'info') return redirect( url_for('components.route_show', component_id=md.component_id, page='keywords'))
def route_checksum_create(component_id): """ Adds a checksum to a component """ # check we have data for key in ['value']: if key not in request.form or not request.form[key]: return _error_internal('No %s specified!' % key) # get firmware component md = db.session.query(Component).\ filter(Component.component_id == component_id).first() if not md: flash('No component matched!', 'danger') return redirect(url_for('firmware.route_firmware')) # security check if not md.check_acl('@modify-checksums'): flash('Permission denied: Unable to modify other vendor firmware', 'danger') return redirect( url_for('components.route_show', component_id=component_id)) # validate is a valid hash hash_value = request.form['value'] if _is_sha1(hash_value): hash_kind = 'SHA1' elif _is_sha256(hash_value): hash_kind = 'SHA256' else: flash('%s is not a recognised SHA1 or SHA256 hash' % hash_value, 'warning') return redirect( url_for('components.route_show', component_id=md.component_id, page='checksums')) # check it's not already been added for csum in md.device_checksums: if csum.value == hash_value: flash('%s has already been added' % hash_value, 'warning') return redirect( url_for('components.route_show', component_id=md.component_id, page='checksums')) # add checksum csum = Checksum(kind=hash_kind, value=hash_value) md.device_checksums.append(csum) md.fw.mark_dirty() md.fw.signed_timestamp = None db.session.commit() _async_sign_fw.apply_async(args=(md.fw.firmware_id, ), queue='firmware') flash('Added device checksum', 'info') return redirect( url_for('components.route_show', component_id=md.component_id, page='checksums'))
def route_issue_create(component_id): """ Adds one or more CVEs to the existing component """ # check we have data for key in ['value']: if key not in request.form or not request.form[key]: return _error_internal('No %s specified!' % key) # get firmware component md = db.session.query(Component).\ filter(Component.component_id == component_id).first() if not md: flash('No component matched!', 'danger') return redirect(url_for('firmware.route_firmware')) # security check if not md.check_acl('@modify-updateinfo'): flash('Permission denied: Unable to modify firmware', 'danger') return redirect( url_for('components.route_show', component_id=component_id)) # add issue for value in request.form['value'].split(','): if value in md.issue_values: flash('Already exists: {}'.format(value), 'info') continue if value.startswith('CVE-'): issue = ComponentIssue(kind='cve', value=value) elif value.startswith('DSA-'): issue = ComponentIssue(kind='dell', value=value) elif value.startswith('LEN-'): issue = ComponentIssue(kind='lenovo', value=value) elif value.startswith('INTEL-SA-'): issue = ComponentIssue(kind='intel', value=value) else: flash('Issue invalid: {}'.format(value), 'danger') return redirect( url_for('components.route_show', component_id=component_id, page='issues')) if issue.problem: flash('Issue invalid: {}'.format(issue.problem.description), 'danger') return redirect( url_for('components.route_show', component_id=component_id, page='issues')) flash('Added {}'.format(value), 'info') md.issues.append(issue) md.fw.mark_dirty() md.fw.signed_timestamp = None db.session.commit() _async_sign_fw.apply_async(args=(md.fw.firmware_id, ), queue='firmware') return redirect( url_for('components.route_show', component_id=md.component_id, page='issues'))
def route_checksum_delete(component_id, checksum_id): # get firmware component csum = db.session.query(Checksum).filter( Checksum.checksum_id == checksum_id).first() if not csum: flash('No checksum matched!', 'danger') return redirect( url_for('components.route_show', component_id=component_id)) # get the component for the checksum md = csum.md if md.component_id != component_id: flash('Wrong component ID for checksum', 'danger') return redirect( url_for('components.route_show', component_id=component_id)) if not md: return _error_internal('No metadata matched!') # security check if not md.check_acl('@modify-checksums'): flash('Permission denied: Unable to modify other vendor firmware', 'danger') return redirect( url_for('components.route_show', component_id=component_id)) # remove chid md.fw.mark_dirty() md.fw.signed_timestamp = None db.session.delete(csum) db.session.commit() _async_sign_fw.apply_async(args=(md.fw.firmware_id, ), queue='firmware') # log flash('Removed device checksum', 'info') return redirect( url_for('components.route_show', component_id=md.component_id, page='checksums'))
def route_issue_autoimport(component_id): # get firmware component md = db.session.query(Component).\ filter(Component.component_id == component_id).first() if not md: flash('No component matched!', 'danger') return redirect(url_for('firmware.route_firmware')) # permission check if not md.check_acl('@modify-updateinfo'): flash('Permission denied: Unable to modify firmware', 'danger') return redirect( url_for('components.route_show', component_id=component_id)) # find any valid CVE numbers in the existing description n_issues = _autoimport_issues(md, 'CVE-', 'cve') n_issues += _autoimport_issues(md, 'DSA-', 'dell') n_issues += _autoimport_issues(md, 'LEN-', 'lenovo') n_issues += _autoimport_issues(md, 'INTEL-SA-', 'intel') n_issues += _autoimport_issues(md, 'INTEL-TA-', 'intel') # success if not n_issues: flash('No issues could be detected', 'info') else: md.fw.mark_dirty() md.fw.signed_timestamp = None db.session.commit() _async_sign_fw.apply_async(args=(md.fw.firmware_id, ), queue='firmware') flash( 'Added {} issues — now review the update description for sanity'. format(n_issues), 'info') return redirect( url_for('components.route_show', component_id=md.component_id, page='update'))
def route_keyword_delete(component_id, keyword_id): # get firmware component kw = db.session.query(Keyword).filter( Keyword.keyword_id == keyword_id).first() if not kw: flash('No keyword matched!', 'danger') return redirect( url_for('components.route_show', component_id=component_id)) # get the firmware for the keyword md = kw.md if md.component_id != component_id: return _error_internal('Wrong component ID for keyword!') if not md: return _error_internal('No metadata matched!') # security check if not md.check_acl('@modify-keywords'): flash('Permission denied: Unable to modify other vendor firmware', 'danger') return redirect( url_for('components.route_show', component_id=component_id)) # remove chid db.session.delete(kw) md.fw.mark_dirty() md.fw.signed_timestamp = None db.session.commit() _async_sign_fw.apply_async(args=(md.fw.firmware_id, ), queue='firmware') # log flash('Removed keyword %s' % kw.value, 'info') return redirect( url_for('components.route_show', component_id=md.component_id, page='keywords'))
def route_requirement_delete(component_id, requirement_id): # get firmware component rq = db.session.query(Requirement).filter( Requirement.requirement_id == requirement_id).first() if not rq: flash('No requirement matched!', 'danger') return redirect( url_for('components.route_show', component_id=component_id)) # get the firmware for the requirement md = rq.md if md.component_id != component_id: return _error_internal('Wrong component ID for requirement!') if not md: return _error_internal('No metadata matched!') # security check if not md.check_acl('@modify-requirements'): flash('Permission denied: Unable to modify other vendor firmware', 'danger') return redirect( url_for('components.route_show', component_id=component_id)) # remove chid db.session.delete(rq) md.fw.mark_dirty() md.fw.signed_timestamp = None db.session.commit() _async_sign_fw.apply_async(args=(md.fw.firmware_id, ), queue='firmware') # log flash('Removed requirement %s' % rq.value, 'info') return redirect( url_for('components.route_show', component_id=md.component_id, page='requires'))
def _upload_firmware(): # verify the user can upload if not _user_can_upload(g.user): flash('User has not signed legal agreement', 'danger') return redirect(url_for('main.route_dashboard')) # used a custom vendor_id if 'vendor_id' in request.form: try: vendor_id = int(request.form['vendor_id']) except ValueError as e: flash( 'Failed to upload file: Specified vendor ID %s invalid' % request.form['vendor_id'], 'warning') return redirect(url_for('upload.route_firmware')) vendor = db.session.query(Vendor).filter( Vendor.vendor_id == vendor_id).first() if not vendor: flash('Failed to upload file: Specified vendor ID not found', 'warning') return redirect(url_for('upload.route_firmware')) else: vendor = g.user.vendor # security check if not vendor.check_acl('@upload'): flash( 'Permission denied: Failed to upload file for vendor: ' 'User with vendor %s cannot upload to vendor %s' % (g.user.vendor.group_id, vendor.group_id), 'warning') return redirect(url_for('upload.route_firmware')) # not correct parameters if not 'target' in request.form: return _error_internal('No target') if not 'file' in request.files: return _error_internal('No file') if request.form['target'] not in ['private', 'embargo', 'testing']: return _error_internal('Target not valid') # find remote, creating if required remote_name = request.form['target'] if remote_name == 'embargo': remote = vendor.remote else: remote = db.session.query(Remote).filter( Remote.name == remote_name).first() if not remote: return _error_internal('No remote for target %s' % remote_name) # if the vendor has uploaded a lot of firmware don't start changing the rules is_strict = len(vendor.fws) < 500 # load in the archive fileitem = request.files['file'] if not fileitem: return _error_internal('No file object') try: ufile = UploadedFile(is_strict=is_strict) for cat in db.session.query(Category): ufile.category_map[cat.value] = cat.category_id for pro in db.session.query(Protocol): ufile.protocol_map[pro.value] = pro.protocol_id for verfmt in db.session.query(Verfmt): ufile.version_formats[verfmt.value] = verfmt ufile.parse(os.path.basename(fileitem.filename), fileitem.read()) except (FileTooLarge, FileTooSmall, FileNotSupported, MetadataInvalid) as e: flash('Failed to upload file: ' + str(e), 'danger') return redirect(request.url) # check the file does not already exist fw = db.session.query(Firmware)\ .filter(or_(Firmware.checksum_upload_sha1 == ufile.fw.checksum_upload_sha1, Firmware.checksum_upload_sha256 == ufile.fw.checksum_upload_sha256)).first() if fw: if fw.check_acl('@view'): flash( 'Failed to upload file: A file with hash %s already exists' % fw.checksum_upload_sha1, 'warning') return redirect('/lvfs/firmware/%s' % fw.firmware_id) flash( 'Failed to upload file: Another user has already uploaded this firmware', 'warning') return redirect(url_for('upload.route_firmware')) # check the guid and version does not already exist fws = db.session.query(Firmware).all() fws_already_exist = [] for md in ufile.fw.mds: provides_value = md.guids[0].value fw = _filter_fw_by_id_guid_version(fws, md.appstream_id, provides_value, md.version) if fw: fws_already_exist.append(fw) # all the components existed, so build an error out of all the versions if len(fws_already_exist) == len(ufile.fw.mds): if g.user.check_acl('@robot') and 'auto-delete' in request.form: for fw in fws_already_exist: if fw.remote.is_public: flash( 'Firmware {} cannot be autodeleted as is in remote {}'. format(fw.firmware_id, fw.remote.name), 'danger') return redirect(url_for('upload.route_firmware')) if fw.user.user_id != g.user.user_id: flash('Firmware was not uploaded by this user', 'danger') return redirect(url_for('upload.route_firmware')) for fw in fws_already_exist: flash('Firmware %i was auto-deleted due to robot upload' % fw.firmware_id) _firmware_delete(fw) else: versions_for_display = [] for fw in fws_already_exist: for md in fw.mds: if not md.version_display in versions_for_display: versions_for_display.append(md.version_display) flash( 'Failed to upload file: A firmware file for this device with ' 'version %s already exists' % ','.join(versions_for_display), 'danger') return redirect('/lvfs/firmware/%s' % fw.firmware_id) # check if the file dropped a GUID previously supported for umd in ufile.fw.mds: new_guids = [guid.value for guid in umd.guids] for md in db.session.query(Component).\ filter(Component.appstream_id == umd.appstream_id): if md.fw.is_deleted: continue for old_guid in [guid.value for guid in md.guids]: if old_guid in new_guids: continue fw_str = str(md.fw.firmware_id) if g.user.check_acl('@qa') or g.user.check_acl('@robot'): flash( 'Firmware drops GUID {} previously supported ' 'in firmware {}'.format(old_guid, fw_str), 'warning') else: flash( 'Firmware would drop GUID {} previously supported ' 'in firmware {}'.format(old_guid, fw_str), 'danger') return redirect(request.url) # allow plugins to copy any extra files from the source archive for cffile in ufile.cabarchive_upload.values(): ploader.archive_copy(ufile.cabarchive_repacked, cffile) # allow plugins to add files ploader.archive_finalize(ufile.cabarchive_repacked, _get_plugin_metadata_for_uploaded_file(ufile)) # dump to a file download_dir = app.config['DOWNLOAD_DIR'] if not os.path.exists(download_dir): os.mkdir(download_dir) fn = os.path.join(download_dir, ufile.fw.filename) cab_data = ufile.cabarchive_repacked.save(compress=True) with open(fn, 'wb') as f: f.write(cab_data) # create parent firmware object settings = _get_settings() target = request.form['target'] fw = ufile.fw fw.vendor = vendor fw.vendor_odm = g.user.vendor fw.user = g.user fw.addr = _get_client_address() fw.remote = remote fw.checksum_signed_sha1 = hashlib.sha1(cab_data).hexdigest() fw.checksum_signed_sha256 = hashlib.sha256(cab_data).hexdigest() fw.is_dirty = True fw.failure_minimum = settings['default_failure_minimum'] fw.failure_percentage = settings['default_failure_percentage'] # fix name for md in fw.mds: name_fixed = _fix_component_name(md.name, md.developer_name_display) if name_fixed != md.name: flash( 'Fixed component name from "%s" to "%s"' % (md.name, name_fixed), 'warning') md.name = name_fixed # verify each component has a version format for md in fw.mds: if not md.verfmt_with_fallback: flash('Component {} does not have required LVFS::VersionFormat'.\ format(md.appstream_id), 'warning') # add to database fw.events.append( FirmwareEvent(remote_id=remote.remote_id, user_id=g.user.user_id)) db.session.add(fw) db.session.commit() # ensure the test has been added for the firmware type ploader.ensure_test_for_fw(fw) # sync everything we added db.session.commit() # asynchronously run _async_test_run_for_firmware.apply_async(args=(fw.firmware_id, )) # send out emails to anyone interested for u in fw.get_possible_users_to_email: if u == g.user: continue if u.get_action('notify-upload-vendor') and u.vendor == fw.vendor: send_email( "[LVFS] Firmware has been uploaded", u.email_address, render_template('email-firmware-uploaded.txt', user=u, user_upload=g.user, fw=fw)) elif u.get_action('notify-upload-affiliate'): send_email( "[LVFS] Firmware has been uploaded by affiliate", u.email_address, render_template('email-firmware-uploaded.txt', user=u, user_upload=g.user, fw=fw)) flash('Uploaded file %s to %s' % (ufile.fw.filename, target), 'info') # asynchronously sign _async_sign_fw.apply_async(args=(fw.firmware_id, ), queue='firmware') # invalidate if target == 'embargo': remote.is_dirty = True g.user.vendor.remote.is_dirty = True db.session.commit() return redirect(url_for('firmware.route_show', firmware_id=fw.firmware_id))
def route_requirement_modify(component_id): """ Adds a requirement to a component """ # check we have data for key in ['kind', 'value']: if key not in request.form: return _error_internal('No %s specified!' % key) if request.form['kind'] not in ['hardware', 'firmware', 'id']: return _error_internal('No valid kind specified!') # get firmware component md = db.session.query(Component).\ filter(Component.component_id == component_id).first() if not md: flash('No component matched!', 'danger') return redirect(url_for('firmware.route_firmware')) # security check if not md.check_acl('@modify-requirements'): flash('Permission denied: Unable to modify other vendor firmware', 'danger') return redirect( url_for('components.route_show', component_id=component_id)) # validate CHID is a valid GUID if request.form['kind'] == 'hardware' and not _validate_guid( request.form['value']): flash( 'Cannot add requirement: %s is not a valid GUID' % request.form['value'], 'warning') return redirect( url_for('components.route_show', component_id=md.component_id, page='requires')) # empty string is None value = request.form['value'] if not value: value = None # check it's not already been added rq = md.find_req(request.form['kind'], value) if rq: if 'version' in request.form: rq.version = request.form['version'] if 'compare' in request.form: if request.form['compare'] == 'any': db.session.delete(rq) db.session.commit() flash('Deleted requirement %s' % rq.value, 'info') return redirect( url_for('components.route_show', component_id=md.component_id, page='requires')) rq.compare = request.form['compare'] db.session.commit() if rq.value: flash('Modified requirement %s' % rq.value, 'info') else: flash('Modified requirement firmware', 'info') else: # add requirement rq = Requirement( kind=request.form['kind'], value=value, compare=request.form.get('compare', None), version=request.form.get('version', None), depth=request.form.get('depth', None), ) md.requirements.append(rq) flash('Added requirement', 'info') # asynchronously sign md.fw.mark_dirty() md.fw.signed_timestamp = None db.session.commit() _async_sign_fw.apply_async(args=(md.fw.firmware_id, ), queue='firmware') return redirect( url_for('components.route_show', component_id=md.component_id, page='requires'))
def route_requirement_create(component_id): """ Adds a requirement to a component """ # check we have data for key in ['kind', 'value']: if key not in request.form or not request.form[key]: return _error_internal('No %s specified!' % key) if request.form['kind'] not in ['hardware', 'firmware', 'id']: return _error_internal('No valid kind specified!') # get firmware component md = db.session.query(Component).\ filter(Component.component_id == component_id).first() if not md: flash('No component matched!', 'danger') return redirect(url_for('firmware.route_firmware')) # security check if not md.check_acl('@modify-requirements'): flash('Permission denied: Unable to modify other vendor firmware', 'danger') return redirect( url_for('components.route_show', component_id=component_id)) # validate CHID is a valid GUID if request.form['kind'] == 'hardware' and not _validate_guid( request.form['value']): flash( 'Cannot add requirement: %s is not a valid GUID' % request.form['value'], 'warning') return redirect( url_for('components.route_show', component_id=md.component_id, page='requires')) # support empty too compare = request.form.get('compare', None) if not compare: compare = None version = request.form.get('version', None) if not version: version = None depth = request.form.get('depth', None) if not depth: depth = None # firmware is unset value value = request.form['value'].strip() if value == 'self': value = None # add requirement rq = Requirement(kind=request.form['kind'], value=value, compare=compare, version=version, depth=depth) md.requirements.append(rq) md.fw.mark_dirty() md.fw.signed_timestamp = None db.session.commit() _async_sign_fw.apply_async(args=(md.fw.firmware_id, ), queue='firmware') flash('Added requirement', 'info') return redirect( url_for('components.route_show', component_id=md.component_id, page='requires'))
def route_modify(component_id): """ Modifies the component properties """ # find firmware md = db.session.query(Component).filter( Component.component_id == component_id).first() if not md: flash('No component matched!', 'danger') return redirect(url_for('firmware.route_firmware')) # security check if not md.check_acl('@modify-updateinfo'): flash('Permission denied: Insufficient permissions to modify firmware', 'danger') return redirect( url_for('components.route_show', component_id=component_id)) # set new metadata values page = 'overview' retry_all_tests = False if 'screenshot_url' in request.form: md.screenshot_url = request.form['screenshot_url'] if 'protocol_id' in request.form: if md.protocol_id != request.form['protocol_id']: md.protocol_id = request.form['protocol_id'] retry_all_tests = True if 'category_id' in request.form: category_id = request.form['category_id'] if not category_id: category_id = None if md.category_id != category_id: md.category_id = category_id retry_all_tests = True if 'screenshot_caption' in request.form: md.screenshot_caption = _sanitize_markdown_text( request.form['screenshot_caption']) if 'install_duration' in request.form: try: md.install_duration = int(request.form['install_duration']) except ValueError as _: md.install_duration = 0 page = 'install_duration' if 'urgency' in request.form: md.release_urgency = request.form['urgency'] page = 'update' if 'description' in request.form: md.release_description = _sanitize_markdown_text( request.form['description']) page = 'update' if 'details_url' in request.form: md.details_url = request.form['details_url'] page = 'update' if 'source_url' in request.form: md.source_url = request.form['source_url'] page = 'update' if 'appstream_id' in request.form: md.appstream_id = request.form['appstream_id'] if 'name' in request.form: md.name = request.form['name'] if 'name_variant_suffix' in request.form: md.name_variant_suffix = request.form['name_variant_suffix'] if 'release_tag' in request.form: md.release_tag = request.form['release_tag'] if 'release_message' in request.form: md.release_message = request.form['release_message'] # the firmware changed protocol if retry_all_tests: for test in md.fw.tests: test.retry() # ensure the test has been added for the new firmware type ploader.ensure_test_for_fw(md.fw) # sync everything we added db.session.commit() # asynchronously run _async_test_run_for_firmware.apply_async(args=(md.fw.firmware_id, )) # modify md.fw.mark_dirty() md.fw.signed_timestamp = None db.session.commit() _async_sign_fw.apply_async(args=(md.fw.firmware_id, ), queue='firmware') flash('Component updated', 'info') return redirect( url_for('components.route_show', component_id=component_id, page=page))