From ef08c492d70c2949f146679a786ca62c358bf6fa Mon Sep 17 00:00:00 2001 From: Patrick Kage Date: Sat, 23 Jan 2021 22:36:21 -0500 Subject: [PATCH] another dump --- .dockerignore | 3 +- Dockerfile | 2 +- app/hacker.py | 224 +++++++++++++-------- app/schema.sql | 6 +- app/templates/hacker/form.html | 103 +++++++++- docker-compose.yml | 4 +- docs/register.2021.hacktheburgh.nginx.conf | 14 ++ makefile | 15 +- requirements.txt | 1 + template.env | 25 +++ 10 files changed, 292 insertions(+), 105 deletions(-) create mode 100644 docs/register.2021.hacktheburgh.nginx.conf create mode 100644 template.env diff --git a/.dockerignore b/.dockerignore index c0724d6..283e9e8 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,4 @@ **/__pycache__ -**/env_voter +**/env_htb21reg instance/ +staging/ diff --git a/Dockerfile b/Dockerfile index 30daae8..179280b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ FROM python:alpine3.7 WORKDIR /app -RUN apk add make +RUN apk add make alpine-sdk libffi-dev --no-cache RUN pip install gunicorn COPY ./requirements.txt ./requirements.txt RUN pip install -r requirements.txt diff --git a/app/hacker.py b/app/hacker.py index 8fb56e0..824d400 100644 --- a/app/hacker.py +++ b/app/hacker.py @@ -5,6 +5,12 @@ from json import dumps import arrow import time +from multiprocessing import Process +import requests +import tempfile +import boto3 +import shutil +import os bp = Blueprint('hacker', __name__, url_prefix='/hacker') @@ -115,26 +121,68 @@ def application(): ) -@bp.route('/application/submit', methods=['POST']) -@hacker_login_required -def submit_application(): +def s3_upload(filename,local_fn,tmpdir): + + s3 = boto3.client( + 's3', + region_name=os.environ['S3_REGION'], + endpoint_url=os.environ['S3_ENDPOINT'], + aws_access_key_id=os.environ['S3_ACCESS_KEY'], + aws_secret_access_key=os.environ['S3_ACCESS_SECRET'], + ) + + response = s3.upload_file(local_fn, os.environ['S3_BUCKET'], filename, ExtraArgs={'ContentType': "application/pdf", 'ACL': "public-read"}) + + shutil.rmtree(tmpdir) + + +def write_application(submit=False): db = get_db() c = db.cursor() - #appl, _ = resolve_application(session['email']) - #if appl['completed']: - # flasher('Can\'t re-submit, application already submitted!', color='warning') - # return redirect(url_for('hacker.application')) + appl_old, _ = resolve_application(session['email']) cfg = get_config(db) if (cfg['applications_dline'] <= time.time()): flasher('Application deadline has passed!', color='danger') return redirect(url_for('hacker.application')) + if request.files['resume'].filename != '': + + filename = f"htb21-cvs/{'_'.join(appl_old['user_id'].split(':'))}.pdf" + + # HAX - S3 upload is slow as fuck, so i'm doing it in the background + # and just hoping it works + print(filename) + + tmpdir = tempfile.mkdtemp() + local_fn = os.path.join(tmpdir, filename.split('/')[1]) + request.files['resume'].save(local_fn) + upload = Process(target=s3_upload, args=(filename,local_fn,tmpdir)) + + upload.daemon = True + upload.start() + + # oh jesus + resume = f"{os.environ['S3_SUBDOMAIN']}/{filename}" + # we're going to set this separately to avoid overriding existing values + c.execute(''' + UPDATE Applicants + SET resume=? + WHERE email=? + ''', (resume, session['email'])) + + #print(equest.files['resume']) + try: + + # checkboxes cb_gdpr = 'gdpr' in request.form and request.form['gdpr'] == 'on' + cb_gdpr_sponsor = 'gdpr_sponsor' in request.form and request.form['gdpr_sponsor'] == 'on' cb_mlh_coc = 'mlh_coc' in request.form and request.form['mlh_coc'] == 'on' + cb_mlh_admin = 'mlh_admin' in request.form and request.form['mlh_admin'] == 'on' + cb_mlh_email = 'mlh_email' in request.form and request.form['mlh_email'] == 'on' cb_adult = 'adult' in request.form and request.form['adult'] == 'on' cb_hackuk_admin = 'hackuk_admin' in request.form and request.form['hackuk_admin'] == 'on' cb_hackuk_email = 'hackuk_email' in request.form and request.form['hackuk_email'] == 'on' @@ -160,11 +208,14 @@ def submit_application(): description=?, essay=?, gdpr=?, + gdpr_sponsor=?, mlh_coc=?, + mlh_admin=?, + mlh_email=?, adult=?, hackuk_admin=?, hackuk_email=?, - completed=1, + completed=?, completed_time=? WHERE email=? @@ -185,16 +236,23 @@ def submit_application(): request.form['description'], request.form['essay'], cb_gdpr, + cb_gdpr_sponsor, cb_mlh_coc, + cb_mlh_admin, + cb_mlh_email, cb_adult, cb_hackuk_admin, cb_hackuk_email, - time.time(), + 1 if submit else 0, + (time.time()) if submit else None, session['email'] ]) c.connection.commit() - flasher('Submitted successfully!', color='success') + if submit: + flasher('Submitted successfully!', color='success') + else: + flasher('Saved successfully!', color='success') except Exception as e: raise e print(e) @@ -202,88 +260,17 @@ def submit_application(): return redirect(url_for('hacker.application')) + +@bp.route('/application/submit', methods=['POST']) +@hacker_login_required +def submit_application(): + write_application(submit=True) + return redirect(url_for('hacker.application')) + @bp.route('/application/save', methods=['POST']) @hacker_login_required def save_application(): - db = get_db() - c = db.cursor() - - appl, _ = resolve_application(session['email']) - if appl['completed']: - flasher('Can\'t save changes, application already submitted!', color='warning') - return redirect(url_for('hacker.application')) - - cfg = get_config(db) - if (cfg['applications_dline'] <= time.time()): - flasher('Application deadline has passed!', color='danger') - return redirect(url_for('hacker.application')) - - try: - # checkboxes - cb_gdpr = 'gdpr' in request.form and request.form['gdpr'] == 'on' - cb_mlh_coc = 'mlh_coc' in request.form and request.form['mlh_coc'] == 'on' - cb_adult = 'adult' in request.form and request.form['adult'] == 'on' - cb_hackuk_admin = 'hackuk_admin' in request.form and request.form['hackuk_admin'] == 'on' - cb_hackuk_email = 'hackuk_email' in request.form and request.form['hackuk_email'] == 'on' - - dd_gradYear = request.form['gradYear'] if 'gradYear' in request.form else None - dd_shirt_size = request.form['shirt_size'] if 'shirt_size' in request.form else None - - c.execute(''' - UPDATE Applicants - SET first_name=?, - last_name=?, - contact_email=?, - school=?, - gradYear=?, - shirt_size=?, - address_line_1=?, - address_line_2=?, - address_line_3=?, - address_city=?, - address_region=?, - address_country=?, - address_pcode=?, - description=?, - essay=?, - gdpr=?, - mlh_coc=?, - adult=?, - hackuk_admin=?, - hackuk_email=? - WHERE - email=? - ''',[ - request.form['first_name'], - request.form['last_name'], - request.form['contact_email'], - request.form['school'], - dd_gradYear, - dd_shirt_size, - request.form['address_line_1'], - request.form['address_line_2'], - request.form['address_line_3'], - request.form['address_city'], - request.form['address_region'], - request.form['address_country'], - request.form['address_pcode'], - request.form['description'], - request.form['essay'], - cb_gdpr, - cb_mlh_coc, - cb_adult, - cb_hackuk_admin, - cb_hackuk_email, - session['email'] - ]) - c.connection.commit() - - flasher('Saved successfully!', color='success') - except Exception as e: - raise - print(e) - flasher('Something went wrong! Please contact tech@hacktheburgh.com.', color='danger') - + write_application(submit=False) return redirect(url_for('hacker.application')) @@ -324,3 +311,62 @@ def invites(): invites=rows, login_type=capitalize_login_provider() ) + +@bp.route('/resume') +@hacker_login_required +def show_resume(): + # this exists solely for people who click the link too fast + # (before it has a chance to upload) + appl, _ = resolve_application(session['email']) + + if appl['resume'] is not None and appl['resume'] != '': + resp = requests.head(appl['resume']) + if resp.status_code != 403: + return redirect(appl['resume']) + else: + return 'Resume processing, please try again in a few seconds.' + else: + return "You haven't uploaded a resume!" + + +@bp.route('/resume/delete') +@hacker_login_required +def delete_resume(): + appl, _ = resolve_application(session['email']) + + db = get_db() + cfg = get_config(db) + if (cfg['applications_dline'] <= time.time()): + flasher('Application deadline has passed!', color='danger') + return redirect(url_for('hacker.application')) + + if appl['resume'] is not None and appl['resume'] != '': + filename = f"htb21-cvs/{'_'.join(appl['user_id'].split(':'))}.pdf" + + s3 = boto3.client( + 's3', + region_name=os.environ['S3_REGION'], + endpoint_url=os.environ['S3_ENDPOINT'], + aws_access_key_id=os.environ['S3_ACCESS_KEY'], + aws_secret_access_key=os.environ['S3_ACCESS_SECRET'], + ) + + s3.delete_object( + Bucket=os.environ['S3_BUCKET'], + Key=filename, + ) + + c = db.cursor() + c.execute(''' + UPDATE Applicants + SET resume=NULL + WHERE email=? + ''', [session['email']]) + c.connection.commit() + + flasher('Resume deleted successfully.', 'success') + + return redirect(url_for('hacker.application')) + else: + return "You haven't uploaded a resume!" + diff --git a/app/schema.sql b/app/schema.sql index f1c9204..68b1e1a 100644 --- a/app/schema.sql +++ b/app/schema.sql @@ -42,13 +42,17 @@ CREATE TABLE Applicants ( gender TEXT, description TEXT, essay TEXT, + resume TEXT, shirt_size TEXT, -- authorizations gdpr INTEGER, + gdpr_sponsor INTEGER, mlh_coc INTEGER, + mlh_admin INTEGER, + mlh_email INTEGER, hackuk_admin INTEGER, - hackuk_email INTEGER, + hackuk_email INTEGER, UNIQUE (user_id), UNIQUE (email) diff --git a/app/templates/hacker/form.html b/app/templates/hacker/form.html index e9c6972..0ec0179 100644 --- a/app/templates/hacker/form.html +++ b/app/templates/hacker/form.html @@ -5,7 +5,7 @@

Application

{% if appl['completed'] == 1 %}Submitted {{ completed_time }}{% else %}Not submitted{% endif %} • Required fields marked with an *.

-
+

Basic Info

@@ -188,13 +188,71 @@

Short Answers

-

Authorizations

+ + +
+ +
+
+
+
+ +
+ {% if appl['resume'] %} + + View uploaded +
+ +
+
+ {% endif %} +
+ {% if appl['resume'] %} + + {% endif %} +
+
+
+ + + +

Authorisations

+

Administration

+
+
+ +
+
@@ -203,8 +261,8 @@

Authorizations

@@ -231,6 +289,25 @@

Authorizations

+

Marketing

+ +
+
+ +
+
+
+
+ +
+
+
@@ -240,6 +317,7 @@

Authorizations

+
{% if appl['completed'] == 1 %} @@ -258,7 +336,7 @@

Authorizations

-{% if appl['completed'] == 0 %} +{% if appl['completed'] != 1 %} {% endif %} + + + {% endblock %} diff --git a/docker-compose.yml b/docker-compose.yml index c4a9b6c..1467547 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3' services: - htb20-voter: - image: htb20-voter + htb21-reg: + image: htb21-reg environment: - "FLASK_PREFERRED_URL_SCHEME=https" - "OAUTHLIB_RELAX_TOKEN_SCOPE=1" diff --git a/docs/register.2021.hacktheburgh.nginx.conf b/docs/register.2021.hacktheburgh.nginx.conf new file mode 100644 index 0000000..a12790d --- /dev/null +++ b/docs/register.2021.hacktheburgh.nginx.conf @@ -0,0 +1,14 @@ + +server { + server_name register.2021.hacktheburgh.com; + + location / { + proxy_pass http://localhost:8099; + proxy_redirect off; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto https; + } +} diff --git a/makefile b/makefile index 951dfda..d1ab039 100644 --- a/makefile +++ b/makefile @@ -1,8 +1,8 @@ STAGING = staging VIRTUALENV = env_h21reg DOCKER_TAG_NAME = htb21-reg -REMOTE = compsoc-admin@bucket.comp-soc.com -REMOTE_DESTINATION = ~/voter +REMOTE = compsoc-admin@bucket.2021.hacktheburgh.com +REMOTE_DESTINATION = ~/registration all: run @@ -26,19 +26,24 @@ clean: upload: clean mkdir -p ${STAGING} docker build . -t ${DOCKER_TAG_NAME} - docker save ${DOCKER_TAG_NAME} -o ${STAGING}/${DOCKER_TAG_NAME}.tar.gz - scp ${STAGING}/${DOCKER_TAG_NAME}.tar.gz ${REMOTE}:${REMOTE_DESTINATION}/${DOCKER_TAG_NAME} + docker save ${DOCKER_TAG_NAME} -o ${STAGING}/${DOCKER_TAG_NAME}.tar + gzip ${STAGING}/${DOCKER_TAG_NAME}.tar + ls -lH ${STAGING}/${DOCKER_TAG_NAME}.tar.gz + scp ${STAGING}/${DOCKER_TAG_NAME}.tar.gz ${REMOTE}:${REMOTE_DESTINATION}/${DOCKER_TAG_NAME}.tar.gz + ssh -t ${REMOTE} 'gzip -df ${REMOTE_DESTINATION}/${DOCKER_TAG_NAME}.tar.gz' connect: ssh ${REMOTE} deploy: upload - ssh -t ${REMOTE} 'cd ${REMOTE_DESTINATION}; sudo docker-compose stop; sudo docker load -i ${DOCKER_TAG_NAME}.tar.gz; sudo docker-compose up -d;' + ssh -t ${REMOTE} 'cd ${REMOTE_DESTINATION}; sudo docker-compose stop; sudo docker load -i ${DOCKER_TAG_NAME}.tar; sudo docker-compose up -d;' init-deploy: ssh -t ${REMOTE} 'mkdir -p ${REMOTE_DESTINATION}' scp docker-compose.yml ${REMOTE}:${REMOTE_DESTINATION} + scp .env ${REMOTE}:${REMOTE_DESTINATION} + scp -r instance ${REMOTE}:${REMOTE_DESTINATION}/instance prod: diff --git a/requirements.txt b/requirements.txt index 2210019..3e44c9f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,3 +5,4 @@ arrow python-dotenv bcrypt Markdown +boto3 diff --git a/template.env b/template.env new file mode 100644 index 0000000..76cf99a --- /dev/null +++ b/template.env @@ -0,0 +1,25 @@ + +# Google auth setup +GOOGLE_CLIENT_ID=set_me_pls +GOOGLE_CLIENT_SECRET=set_me_pls + +# MLH auth setup +MLH_CLIENT_ID=set_me_pls +MLH_CLIENT_SECRET=set_me_pls +MLH_CLIENT_CALLBACK=set_me_pls + +# Github auth setup +GITHUB_CLIENT_ID=set_me_pls +GITHUB_CLIENT_SECRET=set_me_pls + +# S3 setup +S3_ACCESS_KEY=set_me_pls +S3_ACCESS_SECRET=set_me_pls +S3_REGION=ams3 +S3_BUCKET=compsoc +S3_SUBDOMAIN=https://cdn.comp-soc.com +S3_ENDPOINT=https://ams3.digitaloceanspaces.com + +# generic +APP_SECRET_KEY=please_set_me_oh_no +