Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • develop
  • master
2 results

Target

Select target project
  • hodorsec/hodor-backend
  • Vasanth/hodor-webapp
  • skamath/hodor-backend
  • techtocore/hodor-backend
4 results
Select Git revision
  • add-chall
  • add-challenge
  • master
3 results
Show changes
Commits on Source (15)
......@@ -57,7 +57,7 @@ coverage.xml
local_settings.py
# Flask stuff:
instance/
migrations/
.webassets-cache
# Scrapy stuff:
......
### Contributing to this repo
* Fork the repository
* Clone your fork using `https://git.amrita.edu/<your_username>/hodor-webapp`
* Clone your fork using `https://git.amrita.edu/<your_username>/hodor-backend`
* Make a new branch with your changes using `git branch -b <branch_name>`. For
instance, if you are working on adding tests, you should do something like `git branch -b add_tests`.
* Send in a pull-request.
* Make your desired changes.
* Stage changes to git using `git add <filenames>`
* Commit your changes using `git commit -m "<A brief summary of your work>"`
* Push to your fork and the new branch you created like : `git push <url_of_fork> <branch_name>`
* Go to GitLab and send a merge-request!
More instructions coming soon :)
\ No newline at end of file
......@@ -3,10 +3,18 @@
The web application for the Hodor Project
## Quickstart
* Install postgresql database. The installation will depend on your operating system and distribution.
* Clone the repo using `git clone https://git.amrita.edu/hodorsec/hodor-webapp`
* Create a virtualenv (see below for instructions) and activate it.
* After activating it, `pip install --user -r requirements.txt` to
install dependencies.
* To initialize the database, run the following:
```bash
python manage.py db init
python manage.py db migrate
```
This will create a folder called `migrations` in the working environment. In most cases, you do not need to care about what's in there.
* `./runserver.py` to run the app.
* Navigate to `http://127.0.0.1:8080` to watch it in action!
......
# -*- coding: utf-8 -*-
# app/__init__.py
import os
import sys
import time
from termcolor import colored
import sys
from flask_api import FlaskAPI
from flask_sqlalchemy import SQLAlchemy
# local import
from instance.config import app_config
# initialize sql-alchemy
......@@ -21,9 +21,13 @@ application.config.from_object(app_config[config_name])
application.config.from_pyfile('config.py')
application.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db.init_app(application)
application.config['SECRET_KEY'] = 'blehblehbleh'
# Delay the application launch to get the user attention if running in
# testing mode
# Delay the application launch to get the user attention if running in testing mode
if config_name != 'production':
if config_name != 'production' and not os.getenv('SKIP_COUNT'):
for x in range(0, 5):
sys.stdout.write(colored("\rYou are not running the server in production mode. " +
"App will run in {} seconds..".format(5-x), 'red'))
......@@ -32,7 +36,8 @@ if config_name != 'production':
print(colored("\n\nRunning app in {} mode..".format(config_name), 'yellow'))
# This disables the HTML API renderer for flask_api when called through browsers.
# This disables the HTML API renderer for flask_api when called through
# browsers.
if config_name == 'production':
application.config['DEFAULT_RENDERERS'] = [
'flask.ext.api.renderers.JSONRenderer',
......
# -*- coding: utf-8 -*-
from hodor import app
from flask import jsonify
from flask import request, jsonify, abort, make_response
from hodor.models.user import User
from sqlalchemy.exc import IntegrityError
@app.route('/')
def start():
return jsonify({'hello': 'world'})
def _extract_required_fields(user):
filtered_user = dict()
'''
This can be done by directly dumping the dictionary but there's always
a risk of data leak.So, we pick what we need to give out out of the API
'''
filtered_user['username'] = user.username
filtered_user['first_name'] = user.first_name
filtered_user['last_name'] = user.last_name
filtered_user['email'] = user.email
filtered_user['verified_account'] = user.verified_account
return filtered_user
#########################################
# Get all the user from the database #
#########################################
@app.route('/users', methods=['GET', 'POST'])
def get_all_users():
# TODO: Authentication for calling this API endpoint. Admin only.
"""
This function iterates the database to find all users and returns as JSON
:return: Response Code
"""
response = dict()
response['status'] = 200
response['data'] = []
for user in User.get_all():
'''Here, we pass the raw user object to extract only what we need
to _extract_required_vars to filter it'''
response['data'].append(_extract_required_fields(user))
return response
################################################
# Get a specific the user from the database #
################################################
@app.route('/user/<user_slug>', methods=['GET'])
def get_user_by_username(user_slug):
# TODO: Authentication for calling this API endpoint. Admin only.
"""
This function iterates the database to find all users and returns as JSON
:return: Response Code
"""
check_username = str(user_slug).strip()
get_user = User.query.filter_by(username=check_username).first()
if get_user:
requested_user = _extract_required_fields(get_user)
return make_response(jsonify(status=200, data=requested_user), 200)
else:
return make_response(jsonify(status=404, msg="No such user found in database"), 404)
#########################################
# Register a user to database #
#########################################
@app.route('/users/new', methods=['POST'])
def add_new_user():
"""
This function adds a new user
:return: Response Code
"""
newuser = {}
if request.method == "POST":
try:
newuser['username'] = str(request.data.get('username').strip())
newuser['first_name'] = str(request.data.get('first_name').strip())
newuser['last_name'] = str(request.data.get('last_name').strip())
newuser['email'] = str(request.data.get('email').strip())
newuser['password'] = str(request.data.get('password').strip())
newuser['verification_code'] = str(request.data.get(
'verification_code').strip())
except Exception as e:
print(e)
abort(500)
user = User(**newuser)
user.save()
return make_response(jsonify(status=201, msg="User {} successfully added".format(user.username) +
"to database"), 201)
#################################################
# Check for an existing user in the database #
#################################################
@app.route('/users/check', methods=['POST'])
def check_for_existing_user():
errors = []
# If the username field is passed, check the username field.
if request.data.get('username'):
check_username = str(request.data.get('username').strip())
user_check_user = User.query.filter_by(username=check_username).first()
if user_check_user:
errors.append({
'field': 'username',
'error': '{} is taken'.format(check_username)
})
del user_check_user
# If the email field is set, check for duplicate email
if request.data.get('email'):
check_email = str(request.data.get('email').strip())
user_check_email = User.query.filter_by(email=check_email).first()
if user_check_email:
errors.append({
'field': 'email',
'error': '{} exists in the database'.format(check_email)
})
if errors:
return make_response(jsonify(status=400, err=errors), 400)
else:
return jsonify(
status=200,
msg="ok"
)
################################################
# Handle Integrity Exceptions in API #
################################################
@app.errorhandler(IntegrityError)
def handle_sql_assertion_error(err):
try:
'''
err.orig.args is from the DBAPIError class of SQLAlchemy. It usually
contains the original error message.
The below is an attempt to clean up the message and only return the
relevant part to API
'''
try:
errmsg = err.orig.args[0].split('\n')[1][9:]
except IndexError:
errmsg = err.orig.args[0].split('\n')
except IndexError:
errmsg = err.orig.args[0]
return make_response(jsonify(status=400, msg=errmsg), 400)
@app.route('/print', methods=['GET', 'POST'])
......
# -*- coding: utf-8 -*-
from hodor import db
class Challenges(db.Model):
__tablename__= 'challenges'
# Data variables for each challenge
id = db.Column(db.String(32), primary_key=True, unique=True, nullable=False)
name = db.Column(db.String(32), nullable=False)
points = db.Column(db.Integer, nullable=False)
description = db.Column(db.String(2048), nullable=False)
hints = db.Column(db.String(512), nullable=False)
@staticmethod
def save(self):
db.session.add(self)
db.session.commit()
def delete(self):
db.session.delete(self)
db.session.commit()
def get_all():
return Challenges.query.all()
# -*- coding: utf-8 -*-
from hodor import db
from sqlalchemy import inspect
from sqlalchemy_utils import PasswordType
class User(db.Model):
__tablename__ = 'users'
# Values entered by the user
username = db.Column(db.String(32), primary_key=True)
username = db.Column(db.String(32), primary_key=True, nullable=False)
first_name = db.Column(db.String(32), nullable=False)
last_name = db.Column(db.String(32), nullable=False)
email = db.Column(db.String(64), unique=True, nullable=False)
password = db.Column(db.String(64), nullable=False)
country = db.Column(db.String(64))
avatar_url = db.Column(db.String(256))
'''PasswordType is an awesome function. To check for passwords later,
you can just do user['password'] == 'plaintext' for a boolean response.'''
password = db.Column(PasswordType(
schemes=[
'pbkdf2_sha512',
'md5_crypt'
],
deprecated=['md5_crypt']
), nullable=False)
# Platform values
disabled = db.Column(db.Boolean, default=False)
......@@ -20,11 +33,9 @@ class User(db.Model):
def __unicode__(self):
return unicode(self.username)
def __init__(self, name):
"""initialize with name."""
self.name = name
def save(self):
print type(self)
print self.email
db.session.add(self)
db.session.commit()
......@@ -32,6 +43,10 @@ class User(db.Model):
def get_all():
return User.query.all()
def get_all_dict(self):
return {c.key: getattr(self, c.key)
for c in inspect(self).mapper.column_attrs}
def delete(self):
db.session.delete(self)
db.session.commit()
......
# -*- coding: utf-8 -*-
# coding=utf-8
# coding=utf-8
import os
class Config(object):
"""Parent configuration class."""
DEBUG = False
CSRF_ENABLED = True
TESTING = False
SECRET = os.getenv('SECRET')
SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URL')
class DevelopmentConfig(Config):
"""Configurations for Development."""
DEBUG = True
SQLALCHEMY_DATABASE_URI = 'postgresql://localhost/hodor_test'
SECRET = "ChangeThisStringIfYouWant"
TESTING = True
class TestingConfig(Config):
"""Configurations for Testing, with a separate test database."""
TESTING = True
SQLALCHEMY_DATABASE_URI = 'postgresql://localhost/test_db'
DEBUG = True
SECRET = 'ChangeMeMaybe'
class StagingConfig(Config):
"""Configurations for Staging."""
DEBUG = True
class ProductionConfig(Config):
"""Configurations for Production."""
DEBUG = False
TESTING = False
SQLALCHEMY_DATABASE_URI = 'postgresql://localhost/hodor_prod'
SECRET = "TODO: ChangeMeInProduction"
app_config = {
'development': DevelopmentConfig,
'testing': TestingConfig,
'staging': StagingConfig,
'production': ProductionConfig,
}
\ No newline at end of file
from flask_script import Manager # class for handling a set of commands
from flask_migrate import Migrate, MigrateCommand
from hodor import db, create_app
from hodor import db, app
app = create_app(config_name='development')
migrate = Migrate(app, db)
manager = Manager(app)
......