Skip to content

Commit 9e95399

Browse files
committed
created challenges section,with categories & tags. Now using ReprMixin for model representation and TimeMixin for timestamp fields. dockerfile updated
1 parent 2908f96 commit 9e95399

34 files changed

+882
-325
lines changed

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ before_install:
1919

2020
install:
2121
- "pip install -r src/requirements.txt"
22-
- "python src/create_db.py"
22+
- "python src/create_db.test.py"
2323

2424
before_script:
2525
- black . --check
2626
script:
27-
- flake8 . --count --max-line-length=88 --exclude="src/FlaskRTBCTF/utils/__init__.py" --show-source --statistics
27+
- flake8 . --count --max-line-length=88 --show-source --statistics

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ $ black .
4343
```
4444

4545
```bash
46-
$ flake8 src/ ---max-line-length=88 --show-source --statistics
46+
$ flake8 src/ --max-line-length=88 --show-source --statistics
4747
```
4848

4949
if flake8 shows any errors or warnings, please fix the changes in a new commit and squash all the commits into one before submitting the PR.

Dockerfile

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,28 @@
1-
FROM python:3
1+
FROM python:3.8.2-alpine3.11
22

3+
MAINTAINER eshaan7bansal@gmail.com
4+
5+
# Env
6+
RUN export DATABASE_URL="postgres://${DB_USER}:${DB_PASSWORD}@postgres:${DB_PORT}/${DB_NAME}" \
7+
&& export REDIS_URL="redis://redis:6379/0"
8+
9+
# update and install packages
10+
RUN apk update \
11+
&& apk add libpq postgresql-dev \
12+
&& apk add build-base \
13+
&& apk add --no-cache git libssl1.1 g++ make libffi-dev
14+
15+
# Add a new low-privileged user
16+
RUN adduser --shell /sbin/login www-data -DH
17+
18+
# Install RTB-CTF-Framework
319
WORKDIR /usr/src/app
420
COPY src ./
5-
RUN pip install --no-cache-dir -r requirements.txt
6-
EXPOSE 8080
7-
RUN chown -R 1001:1001 .
8-
USER 1001
21+
RUN pip install --no-cache-dir -r requirements.txt \
22+
&& chown -R www-data ./
23+
24+
USER www-data
25+
26+
EXPOSE 8000
927
RUN chmod +x /usr/src/app/docker-entrypoint.sh
1028
ENTRYPOINT [ "/usr/src/app/docker-entrypoint.sh" ]

README.md

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
<a href="https://travis-ci.com/abs0lut3pwn4g3/RTB-CTF-Framework" target="_blank">
1111
<img alt="Build Status" src="https://travis-ci.com/abs0lut3pwn4g3/RTB-CTF-Framework.svg?branch=gssoc20-dev"/>
1212
</a>
13-
<!-- <a href="https://lgtm.com/projects/g/abs0lut3pwn4g3/RTB-CTF-Framework/context:python">
13+
<a href="https://lgtm.com/projects/g/abs0lut3pwn4g3/RTB-CTF-Framework/context:python">
1414
<img alt="Language grade: Python" src="https://img.shields.io/lgtm/grade/python/g/abs0lut3pwn4g3/RTB-CTF-Framework.svg?logo=lgtm&logoWidth=18"/>
15-
</a> -->
15+
</a>
1616
<a href="https://github.com/psf/black" target="_blank">
1717
<img alt="Code style: black" src="https://img.shields.io/badge/code%20style-black-000000.svg"/>
1818
</a>
@@ -30,23 +30,16 @@ The main purpose of this project is to serve as a scoring engine and CTF manager
3030

3131
## Features
3232

33-
##### For CTF hosters
34-
* A page to show relevant details about the machine such as name, IP, OS, points and difficulty level.
33+
* Machines listing with fields: name, IP, OS, points and difficulty level.
34+
* Challenges listing with fields: title, description, URL, points.
35+
* Totally configurable settings such running time, organization details, CTF name.
3536
* Automatic strong password for administrator
3637
* Well implemented controls for administrators providing features such as issuing notifications, database CRUD operations, full fledged logging,
3738
* Simple User Registration/login process, account management, Forgot password functionalities,
3839
* Flag submission (currently 2 flags: user and root),
3940
* Real time scoreboard tracking,
40-
* Easily deployable on Heroku.
41-
42-
##### For Developers & Contributors
43-
* Flask-blueprints for modularity and clean codebase,
44-
* Flask-admin for Admin views and easy realtime management,
45-
* Flask-SQLAlchemy for SQL models,
46-
* Flask-login for session handling,
47-
* Flask-wtf for responsive forms,
48-
* Flask-mail for mail service,
49-
* Flask-bcrypt for password hashing and security,
41+
* Efficient caching so it's fast
42+
* Easily deployable on Heroku.
5043

5144
## Build locally
5245

@@ -95,11 +88,6 @@ Please see [INSTALLATION.md](INSTALLATION.md).
9588

9689
For further guidelines, Please refer to [CONTRIBUTING.md](CONTRIBUTING.md)
9790

98-
## Screenshots
99-
100-
> Why look at static pictures, when you can use a demo ? Visit: <https://rtblivedemo.herokuapp.com/>.
101-
102-
<img src="screenshots/home_ss.png" width=400 />
103-
<img src="screenshots/scoreboard_ss.png" width=400 />
104-
<img src="screenshots/machine_ss.png" width=400 />
91+
## Live Demo
10592

93+
**Live Demo:** <https://rtblivedemo.herokuapp.com/> (login with `admin:admin`)

docker-compose.yml

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,55 @@
11
version: "3"
22

33
services:
4-
rtbd:
4+
rtbctf:
55
build: .
6+
container_name: rtb_gunicorn
7+
restart: unless-stopped
68
ports:
7-
- 80:8080
8-
restart: unless-stopped
9+
- "80:8000"
10+
expose:
11+
- 8000
12+
environment:
13+
- DEBUG=False
14+
- SECRET_KEY=changeme
15+
- DB_USER=eshaan
16+
- DB_PASSWORD=eshaan
17+
- DB_NAME=rtbctf
18+
- DB_PORT=5432
19+
- WORKERS=8
20+
- ADMIN_PASS=admin
21+
depends_on:
22+
- postgres
23+
- redis
24+
25+
postgres:
26+
image: library/postgres:12.1-alpine
27+
container_name: rtb_postgres
28+
restart: unless-stopped
29+
expose:
30+
- "5432"
31+
environment:
32+
- POSTGRES_USER=eshaan
33+
- POSTGRES_PASSWORD=eshaan
34+
- POSTGRES_DB=rtbctf
35+
36+
redis:
37+
image: redis:6.0-rc4-alpine
38+
container_name: rtb_redis
39+
restart: unless-stopped
40+
expose:
41+
- "6379"
42+
43+
44+
# nginx:
45+
# image: library/nginx:1.16.1-alpine
46+
# container_name: rtb_nginx
47+
# restart: unless-stopped
48+
# hostname: nginx
49+
# volumes:
50+
# - ./rtb_nginx_http:/etc/nginx/conf.d/default.conf
51+
# ports:
52+
# - "80:80"
53+
# - "443:443"
54+
# depends_on:
55+
# - rtbctf

rtb_nginx_http

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# the upstream component nginx needs to connect to
2+
upstream flask {
3+
server rtbctf:8000 fail_timeout=30s;
4+
}
5+
6+
7+
server {
8+
listen 80;
9+
10+
server_name rtbctf.com;
11+
12+
location / {
13+
proxy_pass http://localhost:8000/
14+
}
15+
16+
}

screenshots/home_ss.png

-134 KB
Binary file not shown.

screenshots/machine_ss.png

-128 KB
Binary file not shown.

screenshots/scoreboard_ss.png

-90.6 KB
Binary file not shown.

src/FlaskRTBCTF/admin/views.py

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,16 @@
66
from flask_admin.form import SecureForm
77
from flask_admin.contrib.sqla import ModelView
88

9+
from ..utils.cache import cache
10+
from ..utils.helpers import clear_points_cache
11+
912

1013
class BaseModelView(ModelView):
1114
export_types = ("csv", "json")
1215
can_export = True
1316
form_base_class = SecureForm
17+
column_display_pk = True # optional, but I like to see the IDs in the list
18+
form_excluded_columns = ("created_on", "updated_on")
1419

1520
def is_accessible(self):
1621
if not current_user.is_authenticated or not current_user.isAdmin:
@@ -31,6 +36,7 @@ def _handle_view(self, name, **kwargs):
3136

3237

3338
class UserAdminView(BaseModelView):
39+
can_view_details = True
3440
column_exclude_list = ("password",)
3541
form_exclude_list = ("password",)
3642
column_searchable_list = ("username", "email")
@@ -40,8 +46,15 @@ def create_view(self):
4046
flash("Please use registration form for creating new users.", "info")
4147
return redirect("/admin/user")
4248

49+
@staticmethod
50+
def after_model_delete(model):
51+
cache.delete(key="users")
52+
cache.delete(key="scoreboard")
53+
return
54+
4355

4456
class MachineAdminView(BaseModelView):
57+
can_view_details = True
4558
column_searchable_list = ("name", "ip")
4659

4760
@expose("/new/")
@@ -54,6 +67,62 @@ def edit_view(self):
5467
return redirect(url_for("ctf.edit_machine", id=id))
5568

5669

70+
class ChallengeAdminView(BaseModelView):
71+
can_view_details = True
72+
column_searchable_list = ("title", "url")
73+
form_choices = {
74+
"difficulty": [
75+
("easy", "Easy"),
76+
("medium", "Medium"),
77+
("hard", "Hard"),
78+
("insane", "Insane"),
79+
]
80+
}
81+
82+
@staticmethod
83+
def after_model_change(form, model, is_created):
84+
cache.delete(key="challenges")
85+
return
86+
87+
@staticmethod
88+
def after_model_delete(model):
89+
cache.delete(key="challenges")
90+
return
91+
92+
93+
class UserChallengeAdminView(BaseModelView):
94+
column_filters = ("completed",)
95+
column_list = ("user_id", "challenge_id", "completed")
96+
97+
@staticmethod
98+
def after_model_change(form, model, is_created):
99+
if form.completed != model.completed:
100+
clear_points_cache(userId=model.user_id, mode="c")
101+
return
102+
103+
@staticmethod
104+
def after_model_delete(model):
105+
clear_points_cache(userId=model.user_id, mode="c")
106+
return
107+
108+
109+
class UserMachineAdminView(BaseModelView):
110+
column_filters = ("owned_user", "owned_root")
111+
column_list = ("user_id", "machine_id", "owned_user", "owned_root")
112+
113+
@staticmethod
114+
def after_model_change(form, model, is_created):
115+
if (form.owned_user != model.owned_user) or (
116+
form.owned_root != model.owned_root
117+
):
118+
clear_points_cache(userId=model.user_id, mode="m")
119+
return
120+
121+
@staticmethod
122+
def after_model_delete(model):
123+
clear_points_cache(userId=model.user_id, mode="m")
124+
return
125+
126+
57127
class NotificationAdminView(BaseModelView):
58128
column_searchable_list = ("title",)
59-
form_excluded_columns = ("timestamp",)

0 commit comments

Comments
 (0)