summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimotej Lazar <timotej.lazar@fri.uni-lj.si>2019-02-20 03:24:54 +0100
committerTimotej Lazar <timotej.lazar@fri.uni-lj.si>2019-02-20 03:24:54 +0100
commit2dd80251f10b11030abd36e56aff0d1f3a86a754 (patch)
tree881acb33dc4014487eda4faf8c5d383dcd4fdf1c
parentd0c2fc09b6dc0c51167f15361d5a4a4c2050f205 (diff)
Fix and clean up token-based access to params and results
-rwxr-xr-xkpov_judge/add_task.py15
-rwxr-xr-xkpov_judge/test_task.py61
-rwxr-xr-xkpov_judge/web/kpov_judge/kpov_judge.py161
3 files changed, 107 insertions, 130 deletions
diff --git a/kpov_judge/add_task.py b/kpov_judge/add_task.py
index 066efed..5b77668 100755
--- a/kpov_judge/add_task.py
+++ b/kpov_judge/add_task.py
@@ -12,12 +12,17 @@ import pymongo
from bson import Binary
def task_check(results, params):
- data = urllib.parse.urlencode({
+ data = {
'results': json.dumps(results),
- 'params': json.dumps(params)
- }).encode()
- req = urllib.request.Request('{task_url}/{task_name}/results-token.json'.format(task_url=task_url, task_name=task_name), data)
- response = urllib.request.urlopen(req)
+ 'params': json.dumps({k: v for k, v in params.items() if k != 'token'}),
+ }
+ # should be an argument to task_check, but probably better not modify the signature…
+ if 'token' in params:
+ data['token'] = params['token']
+
+ response = urllib.request.urlopen(
+ '{task_url}/{task_name}/results.json'.format(task_url=task_url, task_name=task_name),
+ data=urllib.parse.urlencode(data).encode())
response_dict = json.loads(response.read().decode())
hints = response_dict.get('hints', [])
hints = ['status: ' + response_dict.get('status', '')] + hints
diff --git a/kpov_judge/test_task.py b/kpov_judge/test_task.py
index 28fc513..a049bee 100755
--- a/kpov_judge/test_task.py
+++ b/kpov_judge/test_task.py
@@ -153,40 +153,42 @@ if __name__ == '__main__':
# get stored task parameters
params['task_params'] = params.get('task_params', {})
task_params = params['task_params'].setdefault(task_name, {})
+ tokens = params.setdefault('tokens', {})
# ensure we have a submission token
if task_url.startswith('http'):
- # check if existing token is valid
- if task_params.get('token'):
- response = urllib.request.urlopen(
- '{task_url}/{task_name}/params-token.json'.format(**params),
- data=urllib.parse.urlencode({'params': json.dumps(task_params)}).encode())
- response = json.load(io.TextIOWrapper(response))
- if response:
- # got a good token
- task_params.update(response)
- else:
- # did not get a token, try again with password
- del task_params['token']
-
- # authenticate to get a new token
- if not task_params.get('token'):
- # get the student's ID and password
- # TODO clunky, should refactor all argument-getting stuff
- fetch_params_meta = {'username': {'descriptions': {'si': 'Uporabniško ime', 'en': 'Username'}}}
- params = get_params(params, fetch_params_meta, params['language'])
- fetch_pass_meta = {'password': {'descriptions': {'si': 'Geslo', 'en': 'Password'}, 'masked': True}}
- params_pass = get_params({}, fetch_pass_meta, params['language'])
-
+ n_tries = 3
+ while n_tries > 0:
try:
- http_auth(task_url, params['username'], params_pass['password'])
- response = urllib.request.urlopen('{task_url}/{task_name}/params.json'.format(**params))
- response = json.load(io.TextIOWrapper(response))
- if response:
- task_params.update(response)
+ if tokens.get(task_name):
+ response = urllib.request.urlopen(
+ '{task_url}/{task_name}/params.json'.format(**params),
+ data=urllib.parse.urlencode({'token': tokens.get(task_name)}).encode())
+ response = json.load(io.TextIOWrapper(response))
+ if response:
+ # got params
+ task_params.update(response)
+ break
+ else:
+ # did not get a token, try again with password
+ del tokens[task_name]
+ n_tries -= 1
+
+ if not tokens.get(task_name):
+ # get the student's ID and password
+ # TODO clunky, should refactor all argument-getting stuff
+ fetch_params_meta = {'username': {'descriptions': {'si': 'Uporabniško ime', 'en': 'Username'}}}
+ params = get_params(params, fetch_params_meta, params['language'])
+ fetch_pass_meta = {'password': {'descriptions': {'si': 'Geslo', 'en': 'Password'}, 'masked': True}}
+ params_pass = get_params({}, fetch_pass_meta, params['language'])
+
+ http_auth(task_url, params['username'], params_pass['password'])
+ response = urllib.request.urlopen('{task_url}/{task_name}/token.json'.format(**params))
+ response = json.load(io.TextIOWrapper(response))
+ if response:
+ tokens[task_name] = response['token']
except Exception as ex:
print(ex)
- exit(2)
if basic_args.generate_params:
#prejema lahko samo stringe in ne številk (potrebno je str(int)
@@ -210,6 +212,7 @@ if __name__ == '__main__':
for k in inspect.getargs(task.__code__)[0]:
public_params[k] = task_params[k]
params['task_params'][params['task_name']] = task_params
+
# save parameters for the next run
with open(basic_args.params_file, 'w') as f:
yaml.dump(params, f)
@@ -219,7 +222,9 @@ if __name__ == '__main__':
print('Running task… ')
task_result = task(**public_params)
print('Checking task… ')
+ task_params['token'] = tokens[task_name] # hack to avoid changing task_check signature
score, hints = task_check(task_result, task_params)
+ del task_params['token']
print('Done!')
print()
print('Score: {}'.format(score))
diff --git a/kpov_judge/web/kpov_judge/kpov_judge.py b/kpov_judge/web/kpov_judge/kpov_judge.py
index ddef16d..7415724 100755
--- a/kpov_judge/web/kpov_judge/kpov_judge.py
+++ b/kpov_judge/web/kpov_judge/kpov_judge.py
@@ -71,74 +71,6 @@ def class_tasks(class_id):
return render_template('class_tasks.html', student_id=student_id, tasks=task_list, clas=clas)
-def results_post(class_id, task_id, token, results):
- student_id = flask.app.request.environ.get('REMOTE_USER', 'Nobody')
- db = g.db
- #params = db.task_params.find_one({'class_id': class_id, 'task_id': task_id, 'student_id': student_id})['params']
- print(class_id, task_id, token)
- params = db.task_params.find_one({'class_id': class_id, 'task_id': task_id, 'params.token': token})['params']
- if not params:
- raise Exception('Invalid token.')
-
- if params is None:
- # params = {}
- #else:
- # params = params['params']
- return {'result': 0, 'hints': ['task not found'], status: 'NOT OK'} # no such task
- user_params = json.loads(flask.app.request.form['params'])
- meta = db.task_params_meta.find_one({'task_id': task_id})
- if meta is None:
- meta = {}
- else:
- meta = meta['params']
- for param_name, param_meta in meta.items():
- if param_meta.get('w', False) and param_name in user_params:
- params[param_name] = user_params[param_name]
- try:
- task_check_source = db.task_checkers.find_one({'class_id': class_id, 'task_id': task_id})['source']
- d = {}
- exec(compile(task_check_source, 'checker.py', 'exec'), globals(), d)
- res, hints = d['task_check'](collections.defaultdict(str, results), params)
- except Exception as e:
- hints = ["Checker died: " + str(e)]
- res = 0
- if (isinstance(res, int) or isinstance(res, float)) and res > 0:
- res_status = 'OK'
- else:
- res_status = 'NOT OK'
- db.results.insert({'class_id': class_id, 'task_id': task_id, 'result': res, 'hints': hints, 'status': res_status,'student_id': student_id, 'response': results, 'time': datetime.datetime.now()})
- return {'result': res, 'hints': hints, 'status': res_status}
-
-
-def results_dict(class_id, task_id, token):
- db = g.db
- try:
- task_params = db.task_params.find_one({'class_id': class_id, 'task_id': task_id, 'token': token})
- if not task_params:
- raise Exception('Invalid token.')
- #student_id = flask.app.request.environ.get('REMOTE_USER')
- student_id = task_params['student_id']
- entry = db.results.find_one(
- {'$query': {'class_id': class_id, 'task_id': task_id, 'student_id': student_id}, # vsi uporabniki brez nastavljenega REMOTE_USER (i.e. Apache basic auth) imajo skupne rezultate, napaka?
- '$orderby': {'time': -1}},
- {'result': 1, 'status': 1, 'hints': 1, '_id': 0})
- if entry is None:
- return {'result': 'Naloga ni bila nikdar ocenjena', 'status': 'NOT OK'}
- return entry
- except Exception as e:
- return {'Error': str(e)}
-
-
-@app.route('/tasks/<class_id>/<task_id>/results.json', methods=['GET', 'POST'])
-def results_json(class_id, task_id):
- if flask.app.request.method == 'POST':
- return json.dumps(
- results_post(class_id, task_id,
- json.loads(flask.app.request.form['params']).get('token'),
- json.loads(flask.app.request.form['results'])))
- return json.dumps(results_dict(class_id, task_id, request.args.get('token')))
-
-
@app.route('/tasks/<class_id>/<task_id>/<lang>/setup.<ending>', methods=['GET'])
def setup_svg(class_id, task_id, lang, ending):
db = g.db
@@ -168,22 +100,14 @@ def task_html(class_id, task_id):
return render_template('task.html', task=task_source(class_id, task_id))
-def make_token(student_id):
- # TODO need nginx support, in version 1.11.3, not yet in debian stable
- #import jwt
- #message = {'student_id': student_id}
- #return jwt.encode(message, app.config['JWT_SECRET'], algorithm='HS512').decode('utf-8')
- return str(uuid.uuid4())
-
def get_params(class_id, task_id, student_id, db):
try:
meta = db.task_params_meta.find_one({'class_id': class_id, 'task_id': task_id})['params']
- meta['token'] = {'public': True, 'generated': True, 'type': 'password', 'w': False}
except Exception:
return {'mama': 'ZAKVAJ?'}, {'mama': {'public': True}}
params = db.task_params.find_one({'class_id': class_id, 'task_id': task_id, 'student_id': student_id})
- if params is None:
+ if params is None or 'params' not in params: # TODO try with $exists: params or smth.
try:
gen_params_source = db.gen_params.find_one({'class_id': class_id, 'task_id': task_id})['source']
gen_params_code = compile(gen_params_source, 'generator.py', 'exec')
@@ -302,12 +226,24 @@ def task_greeting(class_id, task_id, lang):
**{p['name']: p['value'] for p in public_params})
-@app.route('/tasks/<class_id>/<task_id>/params.json')
-def params_json(class_id, task_id, student_id=None):
- if not student_id:
- student_id = flask.app.request.environ.get('REMOTE_USER', 'Nobody')
+@app.route('/tasks/<class_id>/<task_id>/token.json')
+def get_token(class_id, task_id):
db = g.db
- params, meta = get_params(class_id, task_id, student_id, db)
+ student_id = flask.app.request.environ.get('REMOTE_USER', 'Nobody')
+ token = str(uuid.uuid4())
+ db.task_params.update({'class_id': class_id, 'task_id': task_id, 'student_id': student_id},
+ {'$set': {'token': token}}, upsert=True)
+ return json.dumps({'token': token})
+
+
+@app.route('/tasks/<class_id>/<task_id>/params.json', methods=['POST'])
+def params_json(class_id, task_id):
+ db = g.db
+ token = flask.app.request.form['token']
+ record = db.task_params.find_one({'class_id': class_id, 'task_id': task_id, 'token': token})
+ if not record:
+ return json.dumps({})
+ params, meta = get_params(record['class_id'], record['task_id'], record['student_id'], db)
shown_params = {}
for name, param in params.items():
if meta.get(name, {'public': False})['public']:
@@ -315,23 +251,54 @@ def params_json(class_id, task_id, student_id=None):
return json.dumps(shown_params)
-@app.route('/tasks/<class_id>/<task_id>/params-token.json', methods=['POST'])
-def params_token_json(class_id, task_id):
+@app.route('/tasks/<class_id>/<task_id>/results.json', methods=['POST'])
+def results_json(class_id, task_id):
db = g.db
- token = json.loads(flask.app.request.form['params']).get('token', '')
- record = db.task_params.find_one({'class_id': class_id, 'task_id': task_id, 'params.token': token})
- if not record:
- return json.dumps({})
- return params_json(record['class_id'], record['task_id'], record['student_id'])
+ token = flask.app.request.form.get('token', '')
+ task = db.task_params.find_one({'class_id': class_id, 'task_id': task_id, 'token': token})
+ if not task:
+ return json.dumps({'result': 0, 'hints': ['invalid token'], 'status': 'NOT OK'})
-@app.route('/tasks/<class_id>/<task_id>/results-token.json', methods=['GET', 'POST'])
-def results_token_json(class_id, task_id):
- db = g.db
- token = json.loads(flask.app.request.form['params']).get('token', '')
- record = db.task_params.find_one({'class_id': class_id, 'task_id': task_id, 'params.token': token})
- if not record:
- return json.dumps({})
- return results_json(class_id, task_id)
+ params = task['params']
+ if params is None:
+ return json.dumps({'result': 0, 'hints': ['no parameters found for task'], 'status': 'NOT OK'}) # no such task
+
+ results = json.loads(flask.app.request.form['results'])
+ user_params = json.loads(flask.app.request.form['params'])
+
+ meta = db.task_params_meta.find_one({'task_id': task_id})
+ if meta is None:
+ meta = {}
+ else:
+ meta = meta['params']
+ for param_name, param_meta in meta.items():
+ if param_meta.get('w', False) and param_name in user_params:
+ params[param_name] = user_params[param_name]
+
+ # hack to get token into task_check function
+ # TODO rethink the API
+ params['token'] = token
+ try:
+ task_check_source = db.task_checkers.find_one({'class_id': class_id, 'task_id': task_id})['source']
+ d = {}
+ exec(compile(task_check_source, 'checker.py', 'exec'), globals(), d)
+ res, hints = d['task_check'](collections.defaultdict(str, results), params)
+ except Exception as e:
+ hints = ["Checker died: " + str(e)]
+ res = 0
+ if (isinstance(res, int) or isinstance(res, float)) and res > 0:
+ res_status = 'OK'
+ else:
+ res_status = 'NOT OK'
+
+ db.results.insert({
+ 'class_id': class_id, 'task_id': task_id,
+ 'result': res, 'hints': hints, 'status': res_status,
+ 'student_id': task['student_id'],
+ 'response': results,
+ 'time': datetime.datetime.now()
+ })
+ return json.dumps({'result': res, 'hints': hints, 'status': res_status})
if __name__ == '__main__':