Skip to content
New issue

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

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

Already on GitHub? # to your account

Graphql filters query ioc #483

Merged
merged 87 commits into from
Jun 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
c570aad
[ADD] add first filter on iocs: iocID
Elise17 May 15, 2024
42f1bcb
[ADD] add filter on iocs: iocUuiD
Elise17 May 15, 2024
d5b48b6
[ADD] add filter on iocs: iocValue
Elise17 May 15, 2024
3c2553f
[ADD] Add first and after parameter
Elise17 May 15, 2024
d31f472
[ADD] add filter on iocs: iocTypeId
Elise17 May 15, 2024
93f239e
[ADD] add filter on iocs: iocDescription
Elise17 May 15, 2024
8ae3f80
[ADD] add filter on iocs: iocTlpId
Elise17 May 15, 2024
fb97dd8
Cleaning test_graphql_iocs_filter_iocTlpId_should_not_fail
Elise17 May 15, 2024
e25ef75
Cleaning test_graphql_iocs_filter_iocDescripiton_should_not_fail
Elise17 May 15, 2024
628e520
Cleaning test_graphql_iocs_filter_iocTypeId_should_not_fail
Elise17 May 15, 2024
57885b4
Cleaning test_graphql_iocs_filter_iocValue_should_not_fail
Elise17 May 15, 2024
00ea08c
Cleaning test_graphql_iocs_filter_iocUuid_should_not_fail
Elise17 May 15, 2024
24d1f64
Cleaning test_graphql_iocs_filter_iocId_should_not_fail
Elise17 May 15, 2024
2684664
[ADD] add filter on iocs: iocTags
Elise17 May 16, 2024
e356c08
[FIX] Fix filter on iocs: iocTags
Elise17 May 16, 2024
30630f1
[FIX] Fix filter on iocs: iocMisp
Elise17 May 16, 2024
36bb064
[FIX] Fix filter on iocs: userId
Elise17 May 16, 2024
d85a83f
[ADD] Add test_graphql_iocs_parameter_totalCount_should_not_fail
Elise17 May 16, 2024
bbfac23
[DEL] Delete test_graphql_iocs_parameter_totalCount
Elise17 May 16, 2024
3f51b40
[ADD] Add test_graphql_update_ioc_should_not_update_typeId
Elise17 May 16, 2024
322d103
[ADD] Add test_graphql_update_ioc_should_not_update_caseId
Elise17 May 16, 2024
245a96e
[ADD] Add test_graphql_update_ioc_should_not_update_iocId
Elise17 May 16, 2024
0745ef3
[ADD] Add test_graphql_update_ioc_should_not_update_tlpId
Elise17 May 16, 2024
50b8de8
[ADD] Add test_graphql_update_ioc_should_not_update_value
Elise17 May 16, 2024
c0cbf01
[ADD] test_graphql_manage_case_filter_api_rest_should_fail
Elise17 May 21, 2024
82ddafb
[MOD] Rename test_graphql_create_case_api_rest_should_fail
Elise17 May 21, 2024
75ee7bb
[MOD] Change last line of test_graphql_update_ioc_should_not_update_v…
Elise17 May 21, 2024
24e38c4
[DEL] Remove SlicedResult
Elise17 May 21, 2024
ddab6a3
[ADD] Add build_filter_case_ioc_query in graphql/iocs.py
Elise17 May 22, 2024
0901be8
[ADD] add iocValue and iocTlpId optional parameters
Elise17 May 22, 2024
44ffa56
[ADD] Add iocTypeId optional parameter
Elise17 May 22, 2024
aebcf8f
[IMP] Divide test_ioc_filter_first and test_ioc_filter_iocValue
Elise17 May 22, 2024
df225f3
[FIX] Fix problem on test_graphql_iocs_filter_first_should_not_fail
Elise17 May 22, 2024
1f9151c
[MOD] Put build_filter_case_ioc_query in business/iocs.py
Elise17 May 22, 2024
18ef201
[FIX] Add import Ioc in business/iocs
Elise17 May 22, 2024
e2e5a4d
[ADD] Add new filter ioc_link_id in iocs
Elise17 May 22, 2024
913e293
[UPD] Improve test_graphql_delete_case_should_not_fail
Elise17 May 24, 2024
0f192a8
[IMP] Improvements and corrections in test.py
Elise17 May 24, 2024
131c253
[IMP] Code cleaning in overview_db.py
Elise17 May 24, 2024
d2d1b22
[IMP] Code cleaning in iocs.py
Elise17 May 24, 2024
3c4599d
[IMP] Code cleaning in cases.py
Elise17 May 24, 2024
70c8459
[IMP] Code cleaning in manage_case_db.py
Elise17 May 24, 2024
678ba85
[IMP] Code cleaning in iocs.py
Elise17 May 24, 2024
3d8221d
[IMP] Using mandatory parameter calling convention
Elise17 May 27, 2024
ecd83fb
[IMP] Using mandatory parameter calling convention
Elise17 May 27, 2024
edf7d45
[IMP] Using private naming convention
Elise17 May 27, 2024
b9155a2
[IMP] Using optional parameter calling convention
Elise17 May 27, 2024
79be57b
[DEL] Removed unnecessary local variable
Elise17 May 27, 2024
41fbaa2
[DEL] Removed unused function
Elise17 May 27, 2024
a8b3129
[IMP] Reformating
Elise17 May 27, 2024
1840958
[IMP] No spaces around *
Elise17 May 27, 2024
440f650
[ADD] Add test_graphql_iocs_should_return_linked_iocs_of_a_case and f…
Elise17 May 28, 2024
38bbc61
[ADD] New modification for ci.yml
Elise17 May 28, 2024
5900c93
[ADD] Add new test for ioc permission
Elise17 May 28, 2024
6a3ca3a
[DEL] Remove test_graphql_case_should_return_success_ioc
Elise17 May 28, 2024
acaa736
[IMP] Change create_user()
Elise17 May 28, 2024
c74890f
[IMP] Clean code()
Elise17 May 28, 2024
60d6d17
[ADD] Add two tests for permission to see a case and change current_u…
Elise17 Jun 5, 2024
ac4181b
[ADD] Add new filter tags on query cases
Elise17 Jun 6, 2024
24fea8a
[ADD] Add new filter openSince on query cases
Elise17 Jun 6, 2024
e3dc251
[IMP] change filter open_since
Elise17 Jun 10, 2024
f64fdd7
[IMP] Change name of test_graphql_case_should_return_success_cases_query
Elise17 Jun 10, 2024
2e20835
[IMP] Rename import
Elise17 Jun 10, 2024
6dee374
[IMP] Rename import caseTags and caseState
Elise17 Jun 10, 2024
0ffbef9
[DEL] Delete date from datetime
Elise17 Jun 10, 2024
7a7c8e0
[IMP] Replace import datetime
Elise17 Jun 10, 2024
4de93da
[FIX] Add case_open_since in filtered_cases
Elise17 Jun 10, 2024
615cdbf
[IMP] Change name of case_tag
Elise17 Jun 11, 2024
12e7f45
[IMP] Change open_since and add test_graphql_case_should_work_with_op…
Elise17 Jun 11, 2024
5bf485f
[IMP] Change build_filter_case_query (parameter)
Elise17 Jun 11, 2024
69e55a9
[IMP] Change import datetime
Elise17 Jun 11, 2024
b278edd
Update ci.yml
Elise17 Jun 17, 2024
4f2f821
[ADD] Migration for mfa secrets and webauthn
whikernel May 23, 2024
c910376
[ADD] Server settings for MFA
whikernel May 28, 2024
ba24a6f
[ADD] Templates for MFA
whikernel May 28, 2024
3a292ab
[ADD] Added back CI on all branches
whikernel May 29, 2024
d6f1dfd
[ADD] Added track activity for MFA
whikernel May 29, 2024
bbc89c9
[IMP] Tracking of server settings change
whikernel May 29, 2024
75f7d1c
[ADD] CSS for MFA setup
whikernel May 29, 2024
16bdd6c
[FIX] UI of MFA setup
whikernel May 30, 2024
9eb86d6
[FIX] Dict differ
whikernel May 30, 2024
25ef0bf
[IMP] Improved CSS mfa verify
whikernel May 30, 2024
9c9f0d1
[ADD] Added back CI on all branches
whikernel May 29, 2024
00444c8
[IMP] Change call function
Elise17 Jun 17, 2024
7948612
[DEL] Remove a s from dictdiffer
Elise17 Jun 17, 2024
aabaa24
[MRG] Merge branch 'develop' into graphql_filters_query_ioc
whikernel Jun 18, 2024
1ff786e
[FIX] MFA Auth
whikernel Jun 18, 2024
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
33 changes: 10 additions & 23 deletions source/app/blueprints/graphql/cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

from base64 import b64decode

from graphene_sqlalchemy import SQLAlchemyObjectType
from graphene_sqlalchemy import SQLAlchemyConnectionField
from graphene.relay import Node
Expand All @@ -29,42 +27,31 @@
from graphene import Float
from graphene import String

from app.business.iocs import build_filter_case_ioc_query
from app.models.cases import Cases
from app.blueprints.graphql.iocs import IOCObject
from app.business.cases import create
from app.business.cases import delete
from app.business.cases import update

from app.blueprints.graphql.iocs import IOCConnection
from app.blueprints.graphql.sliced_result import SlicedResult


class CaseObject(SQLAlchemyObjectType):
class Meta:
model = Cases
interfaces = [Node]

# TODO add filters
iocs = SQLAlchemyConnectionField(IOCConnection)
iocs = SQLAlchemyConnectionField(IOCConnection, ioc_id=Int(), ioc_uuid=String(), ioc_value=String(), ioc_type_id=Int(),
ioc_description=String(), ioc_tlp_id=Int(), ioc_tags=String(), ioc_misp=String(),
user_id=Float(), Linked_cases=Float())

@staticmethod
def resolve_iocs(root, info, **kwargs):
query = IOCObject.get_query(info)
total = query.count()

first = kwargs.get('first')
if not first:
first = total

if kwargs.get('after'):
after = kwargs.get('after')
decode_after = b64decode(after)
start = int(decode_after[16:].decode())
start += 1
else:
start = 0
query_slice = query.slice(start, start + first).all()
return SlicedResult(query_slice, start, total)
def resolve_iocs(root, info, ioc_id=None, ioc_uuid=None, ioc_value=None, ioc_type_id=None, ioc_description=None, ioc_tlp_id=None, ioc_tags=None,
ioc_misp=None, user_id=None, Linked_cases=None, **kwargs):
return build_filter_case_ioc_query(ioc_id=ioc_id, ioc_uuid=ioc_uuid, ioc_value=ioc_value,
ioc_type_id=ioc_type_id, ioc_description=ioc_description,
ioc_tlp_id=ioc_tlp_id, ioc_tags=ioc_tags, ioc_misp=ioc_misp,
user_id=user_id, linked_cases=Linked_cases)


class CaseConnection(Connection):
Expand Down
28 changes: 10 additions & 18 deletions source/app/blueprints/graphql/graphql_route.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from flask import request
from flask_wtf import FlaskForm
from flask import Blueprint
from flask_login import current_user

from graphql_server.flask import GraphQLView
from graphene import ObjectType
Expand Down Expand Up @@ -52,28 +53,19 @@
class Query(ObjectType):
"""This is the IRIS GraphQL queries documentation!"""

cases = SQLAlchemyConnectionField(CaseConnection, classificationId=Float(), clientId=Float(), stateId=Int(),
ownerId=Float(), openDate=String(), name=String(), socId=String(),
severityId=Int())
cases = SQLAlchemyConnectionField(CaseConnection, classification_id=Float(), client_id=Float(), state_id=Int(),
owner_id=Float(), open_date=String(), name=String(), soc_id=String(),
severity_id=Int(), tags=String(), open_since=Int())
case = Field(CaseObject, case_id=Float(), description='Retrieve a case by its identifier')
ioc = Field(IOCObject, ioc_id=Float(), description='Retrieve an ioc by its identifier')

@staticmethod
def resolve_cases(root, info, **kwargs):
case_classification_id = kwargs.get('classificationId')
case_client_id = kwargs.get('clientId')
case_state_id = kwargs.get('stateId')
case_owner_id = kwargs.get('ownerId')
start_open_date = kwargs.get('openDate')
case_name = kwargs.get('name')
case_soc_id = kwargs.get('socId')
case_severity_id = kwargs.get('severityId')
filtered_cases = build_filter_case_query(current_user_id=1, case_classification_id=case_classification_id,
case_customer_id=case_client_id, case_state_id=case_state_id,
case_owner_id=case_owner_id, start_open_date=start_open_date,
case_name=case_name, case_soc_id=case_soc_id,
case_severity_id=case_severity_id)
return filtered_cases
def resolve_cases(root, info, classification_id=None, client_id=None, state_id=None, owner_id=None, open_date=None, name=None, soc_id=None,
severity_id=None, tags=None, open_since=None, **kwargs):
return build_filter_case_query(current_user.id, start_open_date=open_date, end_open_date=None, case_customer_id=client_id, case_ids=None,
case_name=name, case_description=None, case_classification_id=classification_id, case_owner_id=owner_id,
case_opening_user_id=None, case_severity_id=severity_id, case_state_id=state_id, case_soc_id=soc_id,
case_tags=tags, case_open_since=open_since)

@staticmethod
def resolve_case(root, info, case_id):
Expand Down
25 changes: 14 additions & 11 deletions source/app/blueprints/graphql/iocs.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,11 @@ def mutate(root, info, case_id, type_id, tlp_id, value, description=None, tags=N
class IOCUpdate(Mutation):

class Arguments:
ioc_id = Float()
case_id = Float()
type_id = NonNull(Int)
tlp_id = NonNull(Int)
value = NonNull(String)
ioc_id = NonNull(Float)
case_id = NonNull(Float)
type_id = Int()
tlp_id = Int()
value = String()
description = String()
tags = String()
ioc_misp = String()
Expand All @@ -95,12 +95,15 @@ class Arguments:
ioc = Field(IOCObject)

@staticmethod
def mutate(root, info, type_id, tlp_id, value, ioc_id=None, case_id=None, description=None, tags=None, ioc_misp=None, user_id=None, ioc_enrichment=None, modification_history=None):
request = {
'ioc_type_id': type_id,
'ioc_tlp_id': tlp_id,
'ioc_value': value
}
def mutate(root, info, ioc_id, case_id, type_id=None, tlp_id=None, value=None, description=None, tags=None,
ioc_misp=None, user_id=None, ioc_enrichment=None, modification_history=None):
request = {}
if type_id:
request['ioc_type_id'] = type_id
if tlp_id:
request['ioc_tlp_id'] = tlp_id
if value:
request['ioc_value'] = value
if description:
request['ioc_description'] = description
if tags:
Expand Down
31 changes: 15 additions & 16 deletions source/app/blueprints/#/#_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,22 +139,21 @@ def login():

def wrap_login_user(user):

session['username'] = user.user

if 'SERVER_SETTINGS' not in app.config:
app.config['SERVER_SETTINGS'] = get_server_settings_as_dict()

if app.config['SERVER_SETTINGS']['enforce_mfa']:
if "mfa_verified" not in session or session["mfa_verified"] is False:
return redirect(url_for('mfa_verify'))

print(session)
login_user(user)


#regenerate_session()
#print(session)

session['username'] = user.user

caseid = user.ctx_case
session['permissions'] = ac_get_effective_permissions_of_user(user)

Expand Down Expand Up @@ -185,31 +184,26 @@ def wrap_login_user(user):


@app.route('/auth/mfa-setup', methods=['GET', 'POST'])
@login_required
def mfa_setup():
user = current_user
user = _retrieve_user_by_username(username=session['username'])
form = MFASetupForm()

if user.mfa_setup_complete:
return redirect(url_for('dashboard'))

if form.submit() and form.validate():
# if not user.mfa_secrets:
# user.mfa_secrets = pyotp.random_base32()
# db.session.commit()

token = form.token.data
mfa_secret = form.mfa_secret.data
user_password = form.user_password.data
totp = pyotp.TOTP(mfa_secret)
if totp.verify(token):
if totp.verify(token) and bc.check_password_hash(user.password, user_password):
user.mfa_secrets = mfa_secret
user.mfa_setup_complete = True
db.session.commit()
session["mfa_verified"] = False
track_activity(f'MFA setup successful for user {current_user.user}', ctx_less=True, display_in_ui=False)
track_activity(f'MFA setup successful for user {user.user}', ctx_less=True, display_in_ui=False)
return wrap_login_user(user)
else:
flash('Invalid token. Please try again.', 'danger')
track_activity(f'Failed MFA setup for user {user.user}. Invalid token.', ctx_less=True, display_in_ui=False)
flash('Invalid token or password. Please try again.', 'danger')

temp_otp_secret = pyotp.random_base32()
otp_uri = pyotp.TOTP(temp_otp_secret).provisioning_uri(user.email, issuer_name="IRIS")
Expand All @@ -219,22 +213,25 @@ def mfa_setup():
img.save(buf, format='PNG')
img_str = base64.b64encode(buf.getvalue()).decode()

return render_template('mfa_setup.html', form=form, img_data=img_str, otp_setup_key=user.mfa_secrets)
return render_template('mfa_setup.html', form=form, img_data=img_str, otp_setup_key=temp_otp_secret)


@app.route('/auth/mfa-verify', methods=['GET', 'POST'])
def mfa_verify():
if 'username' not in session:

return redirect(url_for('login.login'))

user = _retrieve_user_by_username(username=session['username'])

# Redirect user to MFA setup if MFA is not fully set up
if not user.mfa_secrets or not user.mfa_setup_complete:
flash('MFA setup is required before verification.', 'warning')
track_activity(f'MFA setup required for user {user.user}', ctx_less=True, display_in_ui=False)
return redirect(url_for('mfa_setup'))

form = MFASetupForm()
form.user_password.data = 'not required for verification'

if form.submit() and form.validate():
token = form.token.data
if not token:
Expand All @@ -245,8 +242,10 @@ def mfa_verify():
if totp.verify(token):
session.pop('username', None)
session['mfa_verified'] = True
track_activity(f'MFA verification successful for user {user.user}', ctx_less=True, display_in_ui=False)
return wrap_login_user(user)
else:
track_activity(f'Failed MFA verification for user {user.user}. Invalid token.', ctx_less=True, display_in_ui=False)
flash('Invalid token. Please try again.', 'danger')

return render_template('mfa_verify.html', form=form)
5 changes: 4 additions & 1 deletion source/app/blueprints/#/templates/mfa_setup.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ <h1 class="text-white mb-3">Setup MFA</h1>
<div class=" col-xs-12 col-md-4">
<div class="d-flex flex-column justify-content-center align-items-center" style="height: 100%;">
<h3 class="login__form_title">Your organisation requires to setup MFA</h3>
<p>Scan the QR code with your authenticator app and enter the token.</p>
<p>Scan the QR code with your authenticator app and enter the token and your password.</p>
<form method="POST" action="">
<div class="col-md-12 col-lg-12 col-sm-12">
<div class="form-row ml-2">
Expand All @@ -35,6 +35,9 @@ <h3 class="login__form_title">Your organisation requires to setup MFA</h3>
{{ form.mfa_secret(size=32, class="hidden", style="display:None;") }}
<label for="token">Token</label>
{{ form.token(size=32, class="form-control") }}

<label for="token">Password</label>
{{ form.user_password(class="form-control") }}
</div>
</div>
<div class="form-row ml-2">
Expand Down
1 change: 0 additions & 1 deletion source/app/blueprints/manage/manage_srv_settings_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ def manage_update_settings():
try:

original_settings = srv_settings_schema.dump(server_settings)

new_settings = request.get_json()

differences = list(diff(original_settings, new_settings))
Expand Down
49 changes: 46 additions & 3 deletions source/app/business/iocs.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from marshmallow.exceptions import ValidationError

from app import db
from app.models import Ioc, IocLink
from app.models.authorization import CaseAccessLevel
from app.datamgmt.case.case_iocs_db import add_ioc
from app.datamgmt.case.case_iocs_db import add_ioc_link
Expand Down Expand Up @@ -85,7 +86,7 @@ def update(identifier, request_json, case_identifier):
check_current_user_has_some_case_access_stricter([CaseAccessLevel.full_access])

try:
ioc = get_ioc(identifier, case_identifier)
ioc = get_ioc(identifier, caseid=case_identifier)
if not ioc:
raise BusinessProcessingError('Invalid IOC ID for this case')

Expand All @@ -96,13 +97,13 @@ def update(identifier, request_json, case_identifier):
# validate before saving
ioc_schema = IocSchema()
request_data['ioc_id'] = identifier
ioc_sc = ioc_schema.load(request_data, instance=ioc)
ioc_sc = ioc_schema.load(request_data, instance=ioc, partial=True)
ioc_sc.user_id = current_user.id

if not check_ioc_type_id(type_id=ioc_sc.ioc_type_id):
raise BusinessProcessingError('Not a valid IOC type')

update_ioc_state(caseid=case_identifier)
update_ioc_state(case_identifier)
db.session.commit()

ioc_sc = call_modules_hook('on_postload_ioc_update', data=ioc_sc, caseid=case_identifier)
Expand Down Expand Up @@ -141,3 +142,45 @@ def get_iocs(case_identifier):
check_current_user_has_some_case_access_stricter([CaseAccessLevel.read_only, CaseAccessLevel.full_access])

return get_iocs_by_case(case_identifier)


def build_filter_case_ioc_query(ioc_id: int = None,
ioc_uuid: str = None,
ioc_value: str = None,
ioc_type_id: int = None,
ioc_description: str = None,
ioc_tlp_id: int = None,
ioc_tags: str = None,
ioc_misp: str = None,
user_id: float = None,
linked_cases: float = None
):
"""
Get a list of iocs from the database, filtered by the given parameters
"""
conditions = []
if ioc_id is not None:
conditions.append(Ioc.ioc_id == ioc_id)
if ioc_uuid is not None:
conditions.append(Ioc.ioc_uuid == ioc_uuid)
if ioc_value is not None:
conditions.append(Ioc.ioc_value == ioc_value)
if ioc_type_id is not None:
conditions.append(Ioc.ioc_type_id == ioc_type_id)
if ioc_description is not None:
conditions.append(Ioc.ioc_description == ioc_description)
if ioc_tlp_id is not None:
conditions.append(Ioc.ioc_tlp_id == ioc_tlp_id)
if ioc_tags is not None:
conditions.append(Ioc.ioc_tags == ioc_tags)
if ioc_misp is not None:
conditions.append(Ioc.ioc_misp == ioc_misp)
if user_id is not None:
conditions.append(Ioc.user_id == user_id)

query = Ioc.query.filter(*conditions)

if linked_cases is not None:
return query.join(IocLink, Ioc.ioc_id == IocLink.ioc_id).filter(IocLink.case_id == linked_cases)

return query
2 changes: 1 addition & 1 deletion source/app/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ class Config:
PERMANENT_SESSION_LIFETIME = timedelta(minutes=config.load('IRIS', 'SESSION_TIMEOUT', fallback=1440))
SESSION_COOKIE_SAMESITE = 'Lax'
SESSION_COOKIE_SECURE = True
MFA_ENABLED = config.load('IRIS', 'MFA_ENABLED', fallback=False)
MFA_ENABLED = config.load('IRIS', 'MFA_ENABLED', fallback=False) == 'True'

PG_ACCOUNT = PG_ACCOUNT_
PG_PASSWD = PG_PASSWD_
Expand Down
Loading
Loading