-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
121 changed files
with
40,334 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
FROM node | ||
MAINTAINER Thomas FADY <[email protected]> | ||
|
||
ADD front/app /app | ||
WORKDIR /app | ||
RUN npm install && npm install --only=dev | ||
ENV NODE_OPTIONS --openssl-legacy-provider | ||
RUN npm run build | ||
|
||
|
||
FROM python:3.11-alpine | ||
MAINTAINER Thomas FADY <[email protected]> | ||
|
||
RUN echo "http://dl-4.alpinelinux.org/alpine/v3.14/main" >> /etc/apk/repositories && \ | ||
echo "http://dl-4.alpinelinux.org/alpine/v3.14/community" >> /etc/apk/repositories | ||
|
||
# install chromedriver | ||
RUN apk update | ||
RUN apk add chromium chromium-chromedriver curl bash | ||
|
||
# upgrade pip | ||
RUN pip install --upgrade pip | ||
|
||
RUN pip install pipenv | ||
|
||
RUN adduser --gecos "" --disabled-password simpleuser | ||
WORKDIR /home/simpleuser | ||
COPY --chown=simpleuser Pipfile Pipfile.lock ./ | ||
USER simpleuser | ||
RUN pipenv install | ||
COPY --chown=simpleuser curlbot curlbot | ||
WORKDIR /home/simpleuser/curlbot | ||
RUN export SECRET_KEY=testing ; pipenv run pytest ; for file in $(find -name "test_*.py"); do echo "Delete $file";rm $file; done && for file in $(find -name "*.db"); do echo "Delete $file";rm $file; done | ||
COPY --from=0 /app/dist /home/simpleuser/curlbot/app/front-app | ||
EXPOSE 5000 | ||
ENTRYPOINT export SECRET_KEY=$(head /dev/urandom| md5sum | cut -d ' ' -f1) && pipenv run gunicorn -b :5000 run:app -w 4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
[[source]] | ||
name = "pypi" | ||
url = "https://pypi.org/simple" | ||
verify_ssl = true | ||
|
||
[dev-packages] | ||
|
||
[packages] | ||
flask = "*" | ||
flask-sqlalchemy = "*" | ||
pytest = "*" | ||
pytest-flask = "*" | ||
flask-login = "*" | ||
pylint = "*" | ||
pylint-flask = "*" | ||
names = "*" | ||
gunicorn = "*" | ||
faker = "*" | ||
flask-mail = "*" | ||
pyjwt = "*" | ||
selenium = "*" | ||
|
||
[requires] | ||
python_version = "3.11" |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
from flask import Flask, jsonify | ||
from flask_sqlalchemy import SQLAlchemy | ||
from flask_login import LoginManager | ||
from flask_mail import Mail | ||
from os import getenv | ||
import os | ||
import importlib | ||
|
||
db = SQLAlchemy() | ||
login_manager = LoginManager() | ||
mail = Mail() | ||
|
||
def create_app(challenge="default"): | ||
global mail, db, login_manager | ||
app = Flask(__name__) | ||
|
||
app.config['MAIL_SERVER'] = getenv('MAIL_SERVER', 'localhost') | ||
app.config['MAIL_PORT'] = getenv('MAIL_PORT', 1025) | ||
app.config['MAIL_USERNAME'] = getenv('MAIL_USERNAME', '') | ||
app.config['MAIL_PASSWORD'] = getenv('MAIL_PASSWORD', '') | ||
app.config['MAIL_USE_TLS'] = getenv('MAIL_USE_TLS', False) | ||
app.config['MAIL_USE_SSL'] = getenv('MAIL_USE_SSL', False) | ||
app.config['CHALLENGE'] = getenv("CHALLENGE", challenge) | ||
app.config['UPLOAD_DIR'] = os.path.join(os.path.dirname(os.path.realpath(__file__)), "profiles") | ||
if getenv('SECRET_KEY', '') == 'testing': | ||
app.config['MAIL_SUPPRESS_SEND'] = True | ||
mail.init_app(app) | ||
|
||
app.config.from_object('config') | ||
|
||
db.init_app(app) | ||
|
||
login_manager.init_app(app) | ||
|
||
from app.account.views import profile, auth | ||
app.register_blueprint(profile.profile_blueprint, url_prefix='/api/v1/user') | ||
app.register_blueprint(auth.auth_blueprint, url_prefix='/api/v1/auth') | ||
|
||
from app.main.views import main, root | ||
app.register_blueprint(main.main_blueprint, url_prefix='/api/v1/') | ||
app.register_blueprint(root.root_blueprint, url_prefix='/') | ||
|
||
from app.robots.views import robots | ||
app.register_blueprint(robots.robots_blueprint, url_prefix='/api/v1/') | ||
|
||
@app.errorhandler(404) | ||
def not_found(error): | ||
return jsonify(status="error",message="Page not found"), 404 | ||
|
||
@app.errorhandler(500) | ||
def not_found(error): | ||
return jsonify(status="error",message="Internal Server Error"), 500 | ||
|
||
challenge = importlib.import_module('app.challenge.'+app.config['CHALLENGE']) | ||
|
||
with app.app_context(): | ||
challenge.load(app) | ||
db.create_all() | ||
db_seed() | ||
challenge.seed(app) | ||
|
||
return app | ||
|
||
def db_seed(): | ||
from faker import Faker | ||
from app.account.models import User | ||
from app.robots.models import Robot | ||
from names import get_full_name | ||
from app.utils import get_random_string | ||
fake = Faker() | ||
if len(User.query.filter_by(admin=True).all()) == 0: | ||
admin_user = User.insert_admin(fake.simple_profile()['username'],get_random_string()) | ||
for i in range(10): | ||
name = get_full_name() | ||
description = name+" is a dummy curl bot." | ||
Robot(name=name,description=description,url=fake.url(),credentials="",owner_id=admin_user.id).save() | ||
for u in range(10): | ||
user = User.insert_user(fake.simple_profile()['username'],get_random_string()) | ||
for i in range(10): | ||
name = get_full_name() | ||
description = name+" is a dummy curl bot." | ||
Robot(name=name,description=description,url=fake.url(),credentials="",owner_id=user.id).save() | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import views |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
from app import db, login_manager | ||
from flask_login import UserMixin | ||
from werkzeug.security import generate_password_hash, check_password_hash | ||
|
||
class User(db.Model, UserMixin): | ||
__tablename__ = 'users' | ||
|
||
id = db.Column(db.Integer, primary_key=True) | ||
username = db.Column(db.String(32), index=True) | ||
password_hash = db.Column(db.String(128)) | ||
admin = db.Column(db.Boolean(),default=False) | ||
reset_token = db.Column(db.String(128)) | ||
avatar = db.Column(db.String(128), default="avatar.png") | ||
|
||
robots = db.relationship('Robot', backref='owner', lazy='dynamic') | ||
|
||
def set_password(self, password): | ||
self.password_hash = generate_password_hash(password,'pbkdf2:sha256:1') | ||
|
||
def verify_password(self, password): | ||
return check_password_hash(self.password_hash, password) | ||
|
||
def is_admin(self): | ||
return self.admin == True | ||
|
||
@classmethod | ||
def insert_user(cls, username, password): | ||
user = User( | ||
username=username, | ||
admin=False, | ||
) | ||
user.set_password(password) | ||
db.session.add(user) | ||
db.session.commit() | ||
db.session.refresh(user) | ||
return user | ||
@classmethod | ||
def insert_admin(cls, username, password): | ||
admin = User( | ||
username=username, | ||
admin=True, | ||
) | ||
admin.set_password(password) | ||
db.session.add(admin) | ||
db.session.commit() | ||
db.session.refresh(admin) | ||
return admin | ||
|
||
@login_manager.user_loader | ||
def load_user(user_id): | ||
""" User loader function | ||
Registered within Flask-Login | ||
""" | ||
try: | ||
user_id = int(user_id) | ||
except ValueError: | ||
print("Could not convert data to an integer.") | ||
return None | ||
except: | ||
print("Unkown error when loading the user") | ||
return None | ||
return User.query.get(user_id) |
96 changes: 96 additions & 0 deletions
96
securedev-training-curlbot/curlbot/app/account/test_account.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import pytest | ||
from .models import User | ||
from app import create_app ,db, mail | ||
import os, json | ||
|
||
mimetype = 'application/json' | ||
headers = { | ||
'Content-Type': mimetype, | ||
'Accept': mimetype | ||
} | ||
|
||
@pytest.fixture | ||
def app(): | ||
app = create_app() | ||
app.config.update({ | ||
"TESTING": True, | ||
}) | ||
|
||
yield app | ||
|
||
@pytest.fixture() | ||
def client(app): | ||
return app.test_client() | ||
|
||
def setup_function(): | ||
app = create_app() | ||
with app.app_context(): | ||
db.session.commit() | ||
db.drop_all() | ||
db.create_all() | ||
|
||
def teardown_function(): | ||
os.remove('app.db') | ||
|
||
class TestUserClass: | ||
@classmethod | ||
def setup_class(self): | ||
setup_function() | ||
|
||
@classmethod | ||
def teardown_class(self): | ||
teardown_function() | ||
|
||
def post_json(self,client, url ,data): | ||
return client.post(url, data=json.dumps(data), headers=headers) | ||
|
||
def test_user_is_not_admin(self): | ||
user = User(username="test") | ||
assert user.is_admin() == False | ||
|
||
def test_admin_is_admin(self, app): | ||
user = User.insert_admin("test","test") | ||
assert user.is_admin() == True | ||
|
||
def test_bad_user_login(self,client): | ||
User.insert_admin("admin","toto") | ||
login_request = client.post("/api/v1/auth/login", data=json.dumps({"login": "a", "password": "a"}), headers=headers) | ||
assert login_request.status_code == 401 | ||
assert login_request.json == {'message': 'Wrong credentials', 'status': 'error'} | ||
login_request = client.post("/api/v1/auth/login", data=json.dumps({"login": "admin", "password": "a"}), headers=headers) | ||
assert login_request.status_code == 401 | ||
assert login_request.json == {'message': 'Wrong credentials', 'status': 'error'} | ||
assert client.get("/api/v1/user/profile").status_code == 401 | ||
|
||
def test_bad_good_login_and_logout(self,client, app): | ||
User.insert_user("toto","toto") | ||
login_request = client.post("/api/v1/auth/login", data=json.dumps({"login": "toto", "password": "toto"}), headers=headers) | ||
assert login_request.status_code == 200 | ||
assert login_request.json['status'] == "success" | ||
login = client.get("/api/v1/user/profile") | ||
assert login.status_code == 200 | ||
assert login.json['login'] == "toto" | ||
assert login.json['is_admin'] == False | ||
assert client.get("/api/v1/auth/logout").status_code == 200 | ||
assert client.get("/api/v1/user/profile").status_code == 401 | ||
|
||
def test_user_can_register(self,client): | ||
assert client.post("/api/v1/auth/register", data=json.dumps({"login": "test_register_user", "password": "test_register_user"}), headers=headers).status_code == 200 | ||
login_request = client.post("/api/v1/auth/login", data=json.dumps({"login": "test_register_user", "password": "test_register_user"}), headers=headers) | ||
assert login_request.status_code == 200 | ||
assert login_request.json['status'] == "success" | ||
login = client.get("/api/v1/user/profile") | ||
assert login.status_code == 200 | ||
assert login.json['login'] == "test_register_user" | ||
assert User.query.filter_by(username="test_register_user").first().is_admin() == False | ||
|
||
def test_user_can_reset(self, client): | ||
client.post("/api/v1/auth/register", data=json.dumps({"login": "test_reset_user", "password": "test_reset_user"}), headers=headers) | ||
res = client.post("/api/v1/auth/reset", data=json.dumps({"login": "test_reset_user"}), headers=headers).json | ||
reset_token = res['message'].split(': ')[1] | ||
reset_request = client.post("/api/v1/auth/reset-password", data=json.dumps({"token": reset_token, "password": "newpassword"}), headers=headers) | ||
assert reset_request.status_code == 200 | ||
reset_request = client.post("/api/v1/auth/reset-password", data=json.dumps({"token": reset_token, "password": "newpassword"}), headers=headers) | ||
assert reset_request.status_code == 401 | ||
assert self.post_json(client,"/api/v1/auth/login",{"login": "test_reset_user", "password": "newpassword"}).json['status'] == "success" | ||
|
Empty file.
Oops, something went wrong.