Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upload systems by CSV and OSCAL #1756

Open
wants to merge 31 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
be8e288
Start of uploading a system based on a excel doc or json file, or an …
SergioJFalcon Jun 28, 2022
d734349
Refactor for DRY __create_new_system, better HTML
gregelin Jun 29, 2022
8d39cde
Refactor for DRY __create_new_system, better HTML
gregelin Jun 29, 2022
9a12106
Merge branch 'system-upload' of github.com:govready/govready-q into s…
gregelin Jun 29, 2022
b74106f
Show drag and drop input field temporarily
gregelin Jun 29, 2022
5c34332
wip
SergioJFalcon Jun 29, 2022
0b61024
Create landing page to display system import results
gregelin Jun 29, 2022
9a14b32
Merge branch 'main' into system-upload
SergioJFalcon Jun 29, 2022
257d992
system import from a xlsl and json file
SergioJFalcon Jun 30, 2022
2584ead
wip
SergioJFalcon Jun 30, 2022
0946655
creating systems from a CSV file
SergioJFalcon Jun 30, 2022
4281dad
Code clean up and add example systems import spreadsheet
gregelin Jul 3, 2022
60b297f
Sync recent changes
gregelin Jul 3, 2022
3df4bf0
Remove extra test files
gregelin Jul 3, 2022
c1ecaf6
system templates for CSV and XLSX formats
SergioJFalcon Jul 5, 2022
34bb207
Update Django to 3.2.14 to address vulnerability
gregelin Jul 6, 2022
8c29e6e
Remove Opera from testing
gregelin Jul 6, 2022
908dfbc
updated urllib3 hashes, wip on system imports
SergioJFalcon Jul 8, 2022
91ab146
wip
SergioJFalcon Jul 9, 2022
998355b
Use older selenium 4.2.0 for tests to work
govreadydeploy Jul 9, 2022
6b6314e
Merge pull request #1759 from GovReady/develop-django-upgrade
gregelin Jul 9, 2022
2e229a9
Merge branch 'develop' of github.com:GovReady/govready-q into develop
govreadydeploy Jul 9, 2022
3b28957
Merge branch 'develop' into system-upload
govreadydeploy Jul 9, 2022
fbba6d4
wip - Import of POAMs
SergioJFalcon Jul 11, 2022
c3c14a7
Remove statement edit from proposed system component; Upgrade moment…
gregelin Jul 12, 2022
3211018
Import of System and POA&Ms
SergioJFalcon Jul 13, 2022
3d167b6
Updating System info appropriately
SergioJFalcon Jul 13, 2022
175ba62
Import of POAMs
SergioJFalcon Jul 14, 2022
79ce157
Update CHANGELOG
gregelin Jul 19, 2022
aa34adb
Merge branch 'main' into develop
gregelin Jul 25, 2022
ba88a17
Merge branch 'develop' into system-upload
gregelin Jul 25, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions controls/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,20 @@ class ImportProjectForm(forms.Form):
)
json_content = forms.CharField(label='Project (JSON)', widget=forms.Textarea(), help_text="The JSON necessary for importing a project.")

class ImportSystemForm(forms.Form):

file = forms.FileField(label="Select system file (.json)",
widget=forms.FileInput(
attrs={
'onchange': "fillFileContent(this);",
# 'accept':'application/json'
}
),
required=False
)
file_content = forms.CharField(label='System (File)', widget=forms.Textarea(), help_text="The FILE necessary for importing a system.")
file_name = forms.CharField(label='Imported File Name', widget=forms.HiddenInput(), required=False)

class DeploymentForm(ModelForm):

def __init__(self, *args, **kwargs):
Expand Down
1 change: 1 addition & 0 deletions controls/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
url(r'^(?P<system_id>.*)/components/(?P<element_id>.*)/proposal_message$', views.proposal_message, name="proposal_message"),
url(r'^(?P<system_id>.*)/component/(?P<element_id>.*)$', views.system_element, name="system_element"),
url(r'^(?P<system_id>.*)/controls/updated$', views.controls_updated, name="controls_updated"),
url(r'^system_import$', views.system_import, name="system_import"),

# Component Library
url(r'^components$', views.component_library, name="component_library"),
Expand Down
138 changes: 135 additions & 3 deletions controls/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
from system_settings.models import SystemSettings
from .forms import ElementEditForm, ElementEditAccessManagementForm
from .forms import ImportOSCALComponentForm, SystemAssessmentResultForm
from .forms import StatementPoamForm, PoamForm, ElementForm, DeploymentForm, StatementEditForm
from .forms import StatementPoamForm, PoamForm, ElementForm, DeploymentForm, StatementEditForm, ImportSystemForm, ImportProjectForm
from .models import *
from .utilities import *
from integrations.models import Integration
Expand Down Expand Up @@ -3585,6 +3585,136 @@ def poam_export(request, system_id, format='xlsx'):

# Project
@login_required
def system_import(request):
"""
Import an entire project's components and control content
"""
# TODO: FALCON

# system_id = project.system.id

# Retrieve identified System
# system = System.objects.get(id=system_id)
# system_root_element = system.root_element

# Retrieve identified System
if request.method == 'POST':

file_content = request.POST['file_content']
file_name = request.POST['file_name']
# Need to get or create the app source by the id of the given app source


# module_name = json.loads(project_data).get('project').get('module').get('key')
# title = json.loads(project_data).get('project').get('title')
# system_root_element.name = title

# If file is a JSON file
new_system_name = file_name + " system"
# What is default template?
new_system_msg = f"Created new System in GovReady based with name '{new_system_name}'."
source_slug = "govready-q-files-startpack"
app_name = "speedyssp"
# can user start the app?
# Is this a module the user has access to? The app store
# does some authz based on the organization.
catalog = get_compliance_apps_catalog_for_user(request.user)
for app_catalog_info in catalog:
if app_catalog_info["key"] == source_slug + "/" + app_name:
# We found it.
break
else:
raise Http404()
# Start the most recent version of the app.
appver = app_catalog_info["versions"][0]
organization = Organization.objects.first() # temporary
default_folder_name = "Started Apps"
folder = Folder.objects.filter(
organization=organization,
admin_users=request.user,
title=default_folder_name,
).first()
if not folder:
folder = Folder.objects.create(organization=organization, title=default_folder_name)
folder.admin_users.add(request.user)
task = None
q = None
# Get portfolio project should be included in.
if request.GET.get("portfolio"):
portfolio = Portfolio.objects.get(id=request.GET.get("portfolio"))
else:
if not request.user.default_portfolio:
request.user.create_default_portfolio_if_missing()
portfolio = request.user.default_portfolio
# import ipdb; ipdb.set_trace()
try:
project = start_app(appver, organization, request.user, folder, task, q, portfolio)
except ModuleDefinitionError as e:
error = str(e)
# Associate System with CSAM system
new_system = project.system
new_system.info = {
"newwww": "newwwwww",
}
# new_system.info = {
# "created_from_input": new_system_str,
# "system_description": new_system_description,

# "id": "~",
# "other_id": "~",
# "name": "~",
# "organization_name": "~",
# "aka": "~",
# "impact": "~",
# "status": "~",
# "type": "~",
# "created": "~",
# "hosting_facility": "~",
# "next_audit": "~",
# "next_scan": "~", #"05/01/22",
# "security_scan": "~",
# "pen_test": "~", #"Scheduled for 05/05/22",
# "config_scan": "~",
# "purpose": "~",
# "vuln_new_30days": "~",
# "vuln_new_rslvd_30days": "~",
# "vuln_90days": "~",
# "risk_score": "~",
# "score_1": "~",
# "score_2": "~",
# "score_3": "~",
# "score_4": "~",
# "score_5": "~",
# }
# new_system.add_event("SYS", new_system_msg)
new_system.save()
# Update System name to URL system name
nsre = new_system.root_element
# Make sure system root element name is unique
name_suffix = ""
while Element.objects.filter(name=f"{new_system_name}{name_suffix}").exists():
# Element exists with that name
if name_suffix == "":
name_suffix = 1
else:
name_suffix = str(int(name_suffix)+1)
if name_suffix == "":
nsre.name = new_system_name
else:
nsre.name = f"{new_system_name}{name_suffix}"
nsre.save()
# Update System Project title to CSAM system name
prt = project.root_task
prt.title_override = nsre.name
prt.save()
logger.info(event=f"create_system_from_url url {new_system_name}",
object={"object": "system", "id": new_system.id},
user={"id": request.user.id, "username": request.user.username})
messages.add_message(request, messages.INFO, new_system_msg)

return HttpResponseRedirect("/projects")
# Project
@login_required
def project_import(request, project_id):
"""
Import an entire project's components and control content
Expand All @@ -3604,7 +3734,7 @@ def project_import(request, project_id):
system_root_element.name = title



import ipdb; ipdb.set_trace()
logger.info(
event="project JSON import update",
object={"object": "project", "id": project.id, "title": project.title},
Expand Down Expand Up @@ -4058,7 +4188,9 @@ def create_system_from_string(request):
category["title"], # except if two categories differ only in case, sort case-sensitively
))
context = {
"apps": catalog_by_category
"apps": catalog_by_category,
"import_system_form": ImportSystemForm(),
"import_project_form": ImportProjectForm(),
}
return render(request, "systems/new_system_form.html", context)

Expand Down
53 changes: 26 additions & 27 deletions templates/components/import-component-modal.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,39 +25,38 @@ <h4 class="modal-title" id="invitation_modal_title">Import OSCAL Component</h4>
</div>

{% block scripts %}
<script>
function show_import_component_modal(callback) {
var m = $('#import_component_modal');
$("#import_loading_spinner").hide();
m.modal();
}

<script>
function show_import_component_modal(callback) {
var m = $('#import_component_modal');
$("#import_loading_spinner").hide();
m.modal();
}

function fillJSONContent(file) {
function fillJSONContent(file) {
var fileInput = document.getElementById('json_content')

var filePath = fileInput.value;
var allowedExtensions =
/(\.json)$/i;
var allowedExtensions = /(\.json)$/i;
if (!allowedExtensions.exec(filePath)) {
fileInput.value = '';
show_modal_error("Wrong File Type Error", "Please only provide json data!");
fileInput.value = '';
show_modal_error("Wrong File Type Error", "Please only provide json data!");
} else {
filecontents = fileInput.files[0];
if (filecontents.size < 5000000) {
$('#{{import_form.import_name.auto_id}}').val(filecontents.name);
var reader = new FileReader();
reader.readAsText(filecontents);
reader.onload = function (e) {
$('#{{import_form.json_content.auto_id}}').val(e.target.result);
}
} else {
show_modal_error("File is too large", "Maximum size is under 5MBs");
filecontents = fileInput.files[0];
if (filecontents.size < 5000000) {
$('#{{import_form.import_name.auto_id}}').val(filecontents.name);
var reader = new FileReader();
reader.readAsText(filecontents);
reader.onload = function (e) {
$('#{{import_form.json_content.auto_id}}').val(e.target.result);
}
}
}
function show_spinner() {
$("#import_loading_spinner").show();
} else {
show_modal_error("File is too large", "Maximum size is under 5MBs");
}
</script>
}
}

function show_spinner() {
$("#import_loading_spinner").show();
}
</script>
{% endblock %}
50 changes: 50 additions & 0 deletions templates/drag-n-drop.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<div id="dropContainer">
<div class="file-droppable" align="center">
<i class="glyphicon glyphicon-save fa-3x"></i>
<br>
<div class="upload-text">Select a file or drag here</div>
<br>
<button type="button" class="btn btn-info btn-lg">Select a file</button>
<span>&times;</span>
<input type="file" id="file_content"/>

</div>
</div>
{% block scripts %}
<script>
document.addEventListener("DOMContentLoaded", function() {
document.querySelectorAll('.file-droppable').forEach(function(droppable) {
var originalText = droppable.querySelector('div').innerHTML;
var input = droppable.querySelector('input');
var fileChanged = function() {
var files = input.files;

console.log('files: ', files);
if (files.length) {
droppable.querySelector('span').style.display = 'block';
droppable.querySelector('div').innerHTML = '';
for (var i = 0; i < files.length; i++) {
droppable.querySelector('div').innerHTML += files[i].name + '<br>';
}
droppable.classList.add('filled');
fillFileContent(files)// file content box
} else {
droppable.querySelector('div').innerHTML = originalText;
droppable.classList.remove('filled');
droppable.querySelector('span').style.display = 'none';
}
};

input.addEventListener('change', fileChanged);
fileChanged(input);

droppable.querySelector('span').addEventListener('click', function() {
input.value = '';
fileChanged(input);
});
});

});
</script>

{% endblock %}
Loading