Skip to content

Commit 8f4c6d4

Browse files
authored
Merge pull request #30 from equalitie/develop
Issue #28
2 parents 9427dc6 + d6afb24 commit 8f4c6d4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+926
-264
lines changed

backend/requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Flask==1.1.2
44
Flask-Cors==3.0.9
55
Flask-Session==0.3.2
66
Flask-SocketIO==4.3.2
7-
passlib==1.7.2
7+
passlib==1.7.4
88
PyJWT==1.7.1
99
PyYAML==3.12
1010
redis==3.5.3
@@ -13,3 +13,4 @@ uuid==1.30
1313
baskerville==1.0.0
1414
docker==4.4.4
1515
docker-compose==1.28.5
16+
pyaml-env==1.1.1

backend/src/baskerville_dashboard/app.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def add_guest_org(session):
9999
if not org:
100100
org = Organization()
101101
new_org = True
102-
org.uuid = org_uuid
102+
org.uuid = org.uuid if not new_org else org_uuid
103103
org.name = name
104104
org.registered = org.registered or False
105105
if new_org:
@@ -345,4 +345,4 @@ def clean_up_before_shutdown():
345345

346346

347347
if __name__ == '__main__':
348-
socketio.run(app, host="0.0.0.0", port=5000)
348+
socketio.run(app, host="0.0.0.0", port=5000, use_reloader=False)

backend/src/baskerville_dashboard/auth.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
import uuid as uuid
88

99
from baskerville.util.enums import UserCategoryEnum
10-
from baskerville_dashboard.utils.helpers import ResponseEnvelope
10+
from baskerville_dashboard.utils.helpers import ResponseEnvelope, \
11+
get_user_by_org_uuid, response_jsonified
1112
from flask import current_app as app, session
1213
from baskerville_dashboard.db.manager import SessionManager
1314
from baskerville.db.dashboard_models import User, UserCategory, Organization
@@ -25,6 +26,7 @@ def create_token(user):
2526
"""
2627
payload = {
2728
'sub': user.organization.uuid,
29+
'id': user.id,
2830
'iat': datetime.utcnow(),
2931
'exp': datetime.utcnow() + timedelta(days=1),
3032
'scope': 'admin' if user.is_admin else 'user',
@@ -64,6 +66,7 @@ def decorated_function(*args, **kwargs):
6466
response.status_code = 401
6567
return response
6668
session['org_uuid'] = payload['sub']
69+
session['user_id'] = payload['id']
6770

6871
return f(*args, **kwargs)
6972

@@ -313,3 +316,20 @@ def post(self):
313316
# except RuntimeError as e:
314317
# msg.message = str(e)
315318
# return jsonify(msg.to_dict()), 500
319+
320+
321+
def resolve_user(f):
322+
323+
@wraps(f)
324+
def decorated_function(*args, **kwargs):
325+
from flask import request
326+
request.re = ResponseEnvelope()
327+
user = get_user_by_org_uuid(session['org_uuid'], session['user_id'])
328+
if not user:
329+
code = 404
330+
request.re.success = False
331+
request.re.message = 'No user found'
332+
return response_jsonified(request.re, code)
333+
request.user = user
334+
return f(*args, **kwargs)
335+
return decorated_function

backend/src/baskerville_dashboard/routes/feedback.py

Lines changed: 78 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
import traceback
77

88
from baskerville.util.enums import FeedbackContextTypeEnum, LabelEnum
9-
from baskerville_dashboard.auth import login_required
9+
from baskerville.util.helpers import get_logger
10+
from baskerville_dashboard.auth import login_required, resolve_user
1011
from baskerville_dashboard.db.manager import SessionManager
1112
from baskerville.db.dashboard_models import Feedback, FeedbackContext
1213
from baskerville_dashboard.utils.helpers import ResponseEnvelope, \
@@ -21,9 +22,12 @@
2122

2223
ES_HOST = ''
2324

25+
logger = get_logger(__name__, output_file='baskerville_dashboard.log')
26+
2427

2528
@feedback_app.route('/feedback/context', methods=('GET',))
2629
@login_required
30+
@resolve_user
2731
def get_feedback_context_details():
2832
"""
2933
Get all available feedback context, feedback context type and respective
@@ -34,7 +38,7 @@ def get_feedback_context_details():
3438
sm = SessionManager()
3539
code = 200
3640
try:
37-
re.data = FeedbackContextVM().to_dict()
41+
re.data = FeedbackContextVM(request.user).to_dict()
3842
re.success = True
3943
re.message = 'Feedback context details'
4044
except Exception as e:
@@ -48,6 +52,7 @@ def get_feedback_context_details():
4852

4953
@feedback_app.route('/feedback/context/<id>', methods=('GET',))
5054
@login_required
55+
@resolve_user
5156
def get_feedback_context_by_id(id):
5257
"""
5358
Get a specific feedback context by id
@@ -58,7 +63,20 @@ def get_feedback_context_by_id(id):
5863
sm = SessionManager()
5964
code = 200
6065
try:
61-
re.data = sm.session.query(FeedbackContext).filter_by(id=id).first()
66+
feedback_context_q = sm.session.query(
67+
FeedbackContext
68+
).filter_by(id=id)
69+
if not request.user.is_admin:
70+
feedback_context_q = feedback_context_q.filter_by(
71+
uuid_organization=request.user.organization.uuid
72+
).filter_by(
73+
id_user=request.user.id
74+
)
75+
logger.debug(
76+
f'User {request.user.id} is not admin, filtering feedback_context'
77+
)
78+
re.data = feedback_context_q.first()
79+
6280
if not re.data:
6381
code = 404
6482
raise ValueError(f'Could not find feedback context with id {id}')
@@ -76,6 +94,7 @@ def get_feedback_context_by_id(id):
7694

7795
@feedback_app.route('/feedback/context', methods=('POST',))
7896
@login_required
97+
@resolve_user
7998
def save_feedback_context():
8099
re = ResponseEnvelope()
81100
sm = SessionManager()
@@ -86,6 +105,7 @@ def save_feedback_context():
86105
fc = FeedbackContext()
87106
fc.reason = FeedbackContextTypeEnum[data['reason'].replace(' ', '_')]
88107
fc.uuid_organization = session['org_uuid']
108+
fc.id_user = request.user.id
89109
fc.reason_descr = data['reason_descr']
90110
fc.start = data['start']
91111
fc.stop = data['stop']
@@ -122,14 +142,15 @@ def bulk_feedback(context_id, feedback_str):
122142
data = request.get_json()
123143
errors = []
124144
succeeded = []
125-
user = get_user_by_org_uuid(session['org_uuid'])
145+
user = get_user_by_org_uuid(session['org_uuid'], session['user_id'])
126146
if not user:
127147
code = 404
128148
re.success = False
129149
re.message = 'No user found'
130150
return response_jsonified(re, code)
131151

132152
for id in data['rss']:
153+
local_created = False
133154
rs = sm.session.query(RequestSet).filter_by(id=id).first()
134155
if not rs:
135156
errors.append(id)
@@ -144,6 +165,7 @@ def bulk_feedback(context_id, feedback_str):
144165
else:
145166
feedback = Feedback()
146167
created += 1
168+
local_created = True
147169
feedback.uuid_request_set = rs.uuid_request_set
148170
feedback.id_feedback_context = context_id
149171
feedback.id_user = user.id
@@ -157,7 +179,7 @@ def bulk_feedback(context_id, feedback_str):
157179
feedback.score = rs.score
158180
feedback.attack_prediction = rs.attack_prediction or 42
159181
feedback.feedback = feedback_str
160-
if not updated:
182+
if local_created:
161183
sm.session.add(feedback)
162184
if not errors:
163185
sm.session.commit()
@@ -183,6 +205,7 @@ def bulk_feedback(context_id, feedback_str):
183205

184206
@feedback_app.route('/feedback/<context_id>/<rs_id>/<feedback_str>', methods=('POST', 'PUT'))
185207
@login_required
208+
@resolve_user
186209
def set_feedback_for(context_id, rs_id, feedback_str):
187210
sm = SessionManager()
188211
re = ResponseEnvelope()
@@ -200,10 +223,7 @@ def set_feedback_for(context_id, rs_id, feedback_str):
200223

201224
if not rs:
202225
raise Exception('No such request-set')
203-
user = get_user_by_org_uuid(session['org_uuid'])
204-
if not user:
205-
raise Exception('No user found.')
206-
226+
user = request.user
207227
feedback = sm.session.query(Feedback).filter_by(
208228
uuid_request_set=rs.uuid_request_set
209229
).filter_by(id_user=user.id).first()
@@ -222,7 +242,7 @@ def set_feedback_for(context_id, rs_id, feedback_str):
222242
feedback.low_rate = request.get_json().get('lowRate')
223243
feedback.features = rs.features
224244
feedback.score = rs.score
225-
feedback.attack_prediction = rs.attack_prediction or LabelEnum.unknown
245+
feedback.attack_prediction = rs.attack_prediction or LabelEnum.unknown.value
226246
feedback.feedback = feedback_str
227247
if not updated:
228248
sm.session.add(feedback)
@@ -243,6 +263,7 @@ def set_feedback_for(context_id, rs_id, feedback_str):
243263

244264
@feedback_app.route('/feedback/submit/<context_id>', methods=('POST',))
245265
@login_required
266+
@resolve_user
246267
def submit_feedback_for(context_id):
247268
_q_filter = get_qparams(request)
248269
sm = SessionManager()
@@ -251,7 +272,7 @@ def submit_feedback_for(context_id):
251272
ip_list = None
252273
data = request.get_json()
253274
try:
254-
user = get_user_by_org_uuid(session['org_uuid'])
275+
user = request.user
255276
fc = sm.session.query(FeedbackContext).filter_by(id=context_id).first()
256277
if not fc:
257278
print('TODO: could not find Feedback Context')
@@ -271,6 +292,52 @@ def submit_feedback_for(context_id):
271292
re.data = None
272293
re.success = True
273294
re.message = 'Feedback context details'
295+
except Exception as e:
296+
traceback.print_exc()
297+
re.success = False
298+
message = str(e)
299+
if 'NoBrokersAvailable' in message:
300+
message = 'Kafka: NoBrokersAvailable. Please try again later.'
301+
re.message = message
302+
if code == 200:
303+
code = 500
304+
305+
return response_jsonified(re, code)
306+
307+
308+
@feedback_app.route('/feedback/<context_id>/count', methods=('GET',))
309+
@login_required
310+
@resolve_user
311+
def feedback_count(context_id):
312+
_q_filter = get_qparams(request)
313+
sm = SessionManager()
314+
re = ResponseEnvelope()
315+
code = 200
316+
try:
317+
user = request.user
318+
fc = sm.session.query(FeedbackContext)
319+
if not user.is_admin:
320+
fc = fc.filter_by(
321+
id_user=user.id
322+
)
323+
fc = fc.filter_by(
324+
id=context_id
325+
).first()
326+
if not fc:
327+
code = 403
328+
raise ValueError(
329+
'Could not find feedback context. '
330+
)
331+
332+
feedback_count = sm.session.query(Feedback).filter_by(
333+
id_user=user.id
334+
).filter_by(
335+
id_feedback_context=fc.id
336+
).count()
337+
338+
re.data = feedback_count
339+
re.success = True
340+
re.message = 'Feedback count for current feedback context'
274341
except Exception as e:
275342
traceback.print_exc()
276343
re.success = False

backend/src/baskerville_dashboard/routes/messages.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def all_notifications():
2424
re = ResponseEnvelope()
2525
try:
2626
code = 200
27-
user = get_user_by_org_uuid(session['org_uuid'])
27+
user = get_user_by_org_uuid(session['org_uuid'], session['user_id'])
2828
notifications = sm.session.query(
2929
Message
3030
).filter_by(uuid_organization=session['org_uuid'])

backend/src/baskerville_dashboard/routes/results.py

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import traceback
99

1010
from pathlib import Path
11-
from baskerville_dashboard.auth import login_required
11+
from baskerville_dashboard.auth import login_required, resolve_user
1212
from baskerville_dashboard.utils.helpers import ResponseEnvelope, get_qparams, \
1313
get_rss, response_jsonified, get_active_app, get_extension, is_compressed, \
1414
get_ip_list, unzip, get_user_by_org_uuid
@@ -74,6 +74,7 @@ def upload_file():
7474

7575
@results_app.route('/results', methods=['POST'])
7676
@login_required
77+
@resolve_user
7778
def get_all_results():
7879
_q_filter = get_qparams(request)
7980
re = ResponseEnvelope()
@@ -83,18 +84,29 @@ def get_all_results():
8384
re.message = f'No results found.'
8485
code = 200
8586
ip_list = None
87+
runtime = None
8688
try:
89+
data = request.json
8790
org_uuid = session['org_uuid']
8891
ip_file_name = request.args.get('file')
89-
user = get_user_by_org_uuid(org_uuid)
90-
if not user:
91-
code = 404
92-
re.success = False
93-
re.message = 'No user found'
94-
return response_jsonified(re, code)
92+
app_id = request.args.get('appId')
93+
context_id = data.get('feedbackContextId')
94+
user = request.user
9595
if ip_file_name:
9696
ip_list = get_ip_list(ip_file_name, org_uuid)
97-
re.data = get_rss(**_q_filter, user=user, ip_list=ip_list)
97+
if app_id:
98+
if app_id.startswith(org_uuid):
99+
app_id = app_id.replace(f'{org_uuid}_', '', 1)
100+
runtime = sm.session.query(Runtime).filter(
101+
Runtime.file_name.like(f'%{app_id}%')
102+
).first()
103+
re.data = get_rss(
104+
**_q_filter,
105+
user=user,
106+
ip_list=ip_list,
107+
id_feedback_context=context_id,
108+
id_runtime=runtime.id if runtime else None
109+
)
98110
re.message = f'The request sets for {">>"}'
99111
except Exception as e:
100112
sm.session.rollback()
@@ -108,6 +120,7 @@ def get_all_results():
108120

109121
@results_app.route('/results/<app_id>', methods=['POST'])
110122
@login_required
123+
@resolve_user
111124
def get_results(app_id: str):
112125
from flask import session
113126
_q_filter = get_qparams(request)
@@ -123,7 +136,7 @@ def get_results(app_id: str):
123136
if app_id.startswith(org_uuid):
124137
app_id = app_id.replace(f'{org_uuid}_', '', 1)
125138
ip_file_name = request.args.get('file')
126-
user = get_user_by_org_uuid(org_uuid)
139+
user = get_user_by_org_uuid(org_uuid, session['user_id'])
127140
if not user:
128141
code = 404
129142
re.success = False

backend/src/baskerville_dashboard/routes/retrain.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def retrain():
2929
try:
3030
code = 200
3131
data = request.get_json()
32-
user = get_user_by_org_uuid(session['org_uuid'])
32+
user = get_user_by_org_uuid(session['org_uuid'], session['user_id'])
3333
config = parse_config(data=data['config'])
3434
training_config = TrainingConfig(config).validate()
3535
if len(training_config.errors) > 0:

0 commit comments

Comments
 (0)