Compare commits

...

155 Commits

Author SHA1 Message Date
Danhia 7cf236041f excluded the API from the i18n patterns 2022-02-17 13:09:11 +01:00
Danhia 9d55eb688b added endpoint API for bot 2022-02-17 12:40:40 +01:00
Danhia 58cd330354 removed language code in redirect_uri 2022-02-17 12:21:17 +01:00
Danhia 0c1b47ee1b removed language code in redirect_uri 2022-02-17 12:19:52 +01:00
Danhia 76dc026b73 tentative for fixing url redirect 2022-02-17 12:03:52 +01:00
Danhia 38dec900aa restored discord OAuth, not working in local, let's test on preprod if the url problem is solved 2022-02-17 11:11:36 +01:00
Danhia 7515eb8dce fixed end of sentence in french with quote badly escaped 2022-02-16 20:14:29 +01:00
Danhia 11f9d43560 fixed quotes that where badly escaped in resources 2022-02-16 20:07:19 +01:00
Danhia 2615ad01cd Merge branch 'main' into translation 2022-02-16 19:28:39 +01:00
Danhia 00962b40d3 Merge branch 'main' into challenges-description 2022-02-16 15:41:52 +01:00
Danhia e6402f37dd Merge pull request 'Ressource page translation' (#22) from Starthur/website:main into translation
Reviewed-on: #22
2022-02-16 15:37:18 +01:00
Starthur eb320b7b49 Last push before pull request 2022-02-16 15:23:15 +01:00
Arthur-TRT 2b0d058672 Correct some mistake 2022-02-16 15:19:07 +01:00
Arthur-TRT 210e217660 First translation of Ressources in fr 2022-02-16 15:03:40 +01:00
Danhia a12dd0d020 Merge pull request 'Update README' (#21) from update-readme into main
Reviewed-on: #21
2022-02-16 14:34:14 +01:00
Starthur 377607239e Update README
Better solution to create superuser admin
2022-02-16 14:33:21 +01:00
Starthur 3829ad11ef Update README
Add information about migrate and admin right on local dev
2022-02-16 14:13:00 +01:00
Danhia c7ddf8a435 Update 'requirements.txt' 2022-02-16 13:53:57 +01:00
Danhia 8cada5b6d2 Update 'README.md' 2022-02-16 13:50:19 +01:00
Danhia 678abaa1fc put challenges descriptions outside of the db 2022-02-15 18:13:43 +01:00
Danhia e69a18922c replaced jp with ja for news 2022-02-15 14:27:37 +01:00
Danhia b3583196e8 Merge pull request '[translation] Added Japanese version' (#9) from ysaito/website:translation-jp into translation
Reviewed-on: #9
2022-02-15 13:20:02 +01:00
ysaito 49a66e9565 feat(welcome_ctf_2021.html): Added Japanese version 2022-02-15 18:11:00 +09:00
ysaito 7b2ab40378 feat(sql_challenges.html): Added Japanese version 2022-02-15 18:07:48 +09:00
ysaito 715db01981 feat(speed_dating_ctf_2022.html): Added Japanese version 2022-02-15 18:06:46 +09:00
ysaito 837c625d8e feat(dynamic_scoring.html): Added Japanese version 2022-02-15 18:05:43 +09:00
ysaito 4eceb91f60 feat(42ctf_vm.html): Added Japanese version 2022-02-15 18:04:30 +09:00
Danhia 2f308f75b7 updated all .po with makemessages 2022-02-14 19:38:16 +01:00
Danhia b3a37445a2
Merge pull request #8 from ClemaX/translation/de
German news translation and typos fix
2022-02-14 18:34:08 +00:00
Danhia f417ff59f4 removed start challenge button when event is over 2022-02-14 19:18:00 +01:00
Clément Hamada 76533f42b7 news: locale: de: Initial translation 2022-02-13 21:47:03 +01:00
Clément Hamada 8ed0fb1d64 locale: de: Fix some typos 2022-02-13 21:30:40 +01:00
Danhia 6d68192f2a Merge branch 'main' of https://github.com/Danhia/42CTF into main 2022-02-13 13:14:24 +01:00
Danhia 1fb9f410ac hotfix for 500 when joining team 2022-02-13 13:13:54 +01:00
Danhia bdcfa253b9
Update README.md 2022-02-12 20:35:41 +01:00
Danhia cf4860b8ff
Merge pull request #7 from Miliviu/main
Set Spanish to done in the readme todo list
2022-02-12 19:28:07 +00:00
Danhia b36c38fd60 dynamic scoring for events, i'm pretty sure everything is gonna break 2022-02-12 19:56:46 +01:00
Miliviu 91ec913b8e
Set Spanish to done in the readme todo list 2022-02-11 19:55:50 +01:00
Danhia d86d5f4e92 challenges not yet published 2022-02-09 14:14:41 +01:00
Danhia 01edb75cc6
Merge pull request #6 from Miliviu/main
Add translation of news to Spanish
2022-02-09 13:36:45 +01:00
Miliviu 40fcf1e07a Add translation of news to Spanish 2022-02-09 13:08:02 +01:00
Danhia d77d9386f0 added english version of news contents 2022-02-09 12:39:12 +01:00
Danhia 947650f07d news content is now out of the db for languages other than english 2022-02-09 12:22:12 +01:00
Danhia 2dec4de04f
Merge pull request #5 from Miliviu/main
All Spanish strings translated
2022-02-09 11:12:32 +01:00
Miliviu 5e75e9f411 Add trasnlator references and date of translation 2022-02-09 10:58:12 +01:00
Miliviu 973af232b5 Add Spanish to availible languages in the Settings.py file 2022-02-09 10:53:11 +01:00
Miliviu 41377a56c5 Added spanish translation to both django files 2022-02-09 10:44:40 +01:00
Danhia c8a171fa76 added option to open team to automatching 2022-02-08 15:36:06 +01:00
Danhia dae0a87548 added japanese empty files 2022-02-06 23:39:46 +01:00
Danhia aedbdf57ca fixed redirection when leaving team + few details in html 2022-02-04 19:28:09 +01:00
Danhia 9b938c7f67 updated .po files for last modifications 2022-02-04 19:05:11 +01:00
Danhia f9ae6edc13 Merge branch 'main' into events 2022-02-04 19:04:20 +01:00
Danhia ef1242af46 fixed redirection in events 2022-02-04 19:04:17 +01:00
ix 09cfc722c7 fix event html 2022-02-04 17:13:50 +01:00
Danhia 5010213a13 fixed centering flag vertically 2022-02-04 16:04:56 +01:00
ix ae9a28fc80 fix flags 2022-02-04 15:43:56 +01:00
Danhia a5c97c4e38 beginning of lang flag refacto 2022-02-04 15:28:06 +01:00
Danhia 559c39c4f9
Merge pull request #4 from ClemaX/translation/de
locale: de: Translate all existing messages
2022-02-04 13:05:05 +01:00
Clément Hamada 54226b8904 resources: locale: de: Translate all existing messages 2022-02-04 06:02:00 +01:00
Clément Hamada ca41f8f924 locale: de: Spellcheck 2022-02-04 05:58:07 +01:00
Clément Hamada cb0c2ee639 resources: locale: de: Update messages 2022-02-04 03:34:01 +01:00
Clément Hamada d8e5236bac locale: de: Update file references 2022-02-04 03:30:49 +01:00
Clément Hamada de249cab9c locale: de: Translate all existing messages 2022-02-04 03:13:51 +01:00
Danhia 358de109db changed position of logos in footer for mobile devices + added helloasso button in resources 2022-02-04 00:27:29 +01:00
ix 74cb657501 Merge branch 'main' of github.com:Danhia/42CTF into main 2022-02-03 21:03:18 +01:00
Danhia d5e2554602 hotfix for dynamic scoring 2022-02-03 21:02:55 +01:00
ix c6bbc2a9a1 Fix resources page 2022-02-03 21:00:04 +01:00
ix 65214b46d4 fix spaces between nav elements 2022-02-03 20:49:24 +01:00
ix 0ebf804976 Fix resources page 2022-02-03 20:49:02 +01:00
ix 0d9057eac4 Fix unused html 2022-02-03 20:45:56 +01:00
Danhia 2f6362360b added dynamic scoring for permanent platform, let's hope nothing breaks 2022-02-03 20:27:55 +01:00
Danhia a639904ee2 fixed latest flags when chall is disabled + fixed recompute_scoreboard 2022-02-03 18:46:37 +01:00
Danhia 63d22cdf6d added disabled attribute for challenge + fixed recomputescoreboard 2022-02-03 18:37:24 +01:00
Danhia 911c9f6732 hotfix for 0 division when there is an empty category in profile page 2022-02-02 19:34:22 +01:00
Danhia 6337a0dcdb added different color for members almost everywhere 2022-02-02 19:23:13 +01:00
Danhia c25f58341d replaced flags counter by flag because css is broken on medium devices 2022-01-31 19:27:20 +01:00
Danhia 2ab42b20c7 you can now delete your account 2022-01-31 19:21:14 +01:00
Danhia c38cc8dc94 hotfix for command recompute_scoreboard 2022-01-31 18:46:34 +01:00
Danhia 396d8ee99b Merge branch 'home-refacto' into main 2022-01-30 21:43:20 +01:00
Danhia da6e51bdcd fixed links css for challenges in home 2022-01-30 19:22:58 +01:00
Danhia 0beafc39fa changed timezone in settings.py 2022-01-30 18:22:59 +01:00
Danhia b352a2341a added weekly top 5 + latest flags to home page 2022-01-30 17:59:02 +01:00
Danhia 0c08fdda96 hotfix for too long pseudos breaking top10 css 2022-01-24 21:11:30 +01:00
Danhia 314a7e17b4 fixed broken responsive design for footer 2022-01-24 17:20:23 +01:00
Danhia 3c4478dddb hotfix for duplicate messages in .po files 2022-01-24 16:16:25 +01:00
Danhia 6455a8ddc6 replaced subscribe by register for events 2022-01-24 16:11:03 +01:00
Danhia 4802218005 added event filter for teams in admin panel 2022-01-24 12:59:09 +01:00
Danhia 5242150b92 added event filter for teams in admin panel 2022-01-24 12:57:14 +01:00
Danhia c06c94e807 updtaed footer with twitter and linkedin 2022-01-24 10:27:38 +01:00
Danhia 5219778d5b fixed missing og_img for page preview 2022-01-24 09:57:03 +01:00
Danhia a1bda8a7cc corrected typo in no solves message 2022-01-23 18:48:55 +01:00
Danhia aea1ecda15 changed color in top 10 + updated resources 2022-01-23 18:45:10 +01:00
Danhia 79c74e1946 added member and visitor status + different colors in scoreboard 2022-01-23 18:40:57 +01:00
Danhia 94a24376d4 finished first version of resources, let's deploy ! 2022-01-23 17:08:13 +01:00
Danhia bd4dadeaf1 Merge branch 'main' into ressources 2022-01-23 15:48:57 +01:00
Danhia 827068f113 Merge branch 'events' into main 2022-01-16 22:19:38 +01:00
Danhia 818be801b2 split views.py in events + new field in events model for automatching 2022-01-16 22:18:39 +01:00
Danhia 6a16e0fdbd added auto matching for teams in event 2022-01-15 00:44:25 +01:00
Danhia 40a7d4ebdd Merge branch 'events' into main 2022-01-14 23:42:53 +01:00
Danhia a6095cc5c4 removed 500 when user is not connected and want to access public event 2022-01-14 23:41:59 +01:00
Danhia 66a9bd6b09 first draft of resources 2022-01-08 14:12:09 +01:00
Danhia ceede3cfa0 updated fr translations for events 2022-01-08 11:23:20 +01:00
Danhia ef1673dbd4 updated translation files 2022-01-08 10:55:57 +01:00
Danhia e4f68edeff fixed error when displaying challenge info in solo mode 2022-01-02 21:53:38 +01:00
Danhia 2ab410efdf fixed starting date for graph on event's profile 2022-01-02 21:46:23 +01:00
Danhia dfa5c3830d fixed case where a category has no challenge in an event and profile crash 2022-01-02 21:41:32 +01:00
Danhia 6ec1a20e74 fixed missing team when event is not password protected 2022-01-02 21:00:02 +01:00
Danhia 7ae36e2cee it's possible to modify and leave team 2022-01-02 20:42:10 +01:00
Danhia 411896bf01 you can create and join team + eventplayer and team profile 2021-12-28 20:17:39 +01:00
Danhia 9405fcabde first draft for team events 2021-12-17 19:02:19 +01:00
Danhia 727e11a29e Merge branch 'main' of https://github.com/Danhia/42CTF into main 2021-12-14 21:22:03 +01:00
Danhia be0fc5dd67 added rank on profile page 2021-12-14 21:21:29 +01:00
Danhia d20b3f75d9
Update README.md 2021-12-14 20:38:03 +01:00
Danhia 3d456ea26b little fix for flag length + restrict access for unpublished challenges 2021-12-14 20:35:59 +01:00
ix e5c4a2856e fix access to event challenges when event not begun or user not connected 2021-12-10 09:47:45 +01:00
Danhia 7cf7fd82d4
Merge pull request #3 from Lindoriel/main
correcting syntax error
2021-10-25 21:01:04 +02:00
Maxime ROTH ca34f76465
correcting ". error 2021-10-25 20:58:59 +02:00
Danhia 0123edd87a
Merge pull request #2 from Lindoriel/main
adding french translation
2021-10-25 20:52:14 +02:00
Maxime ROTH a295321f19
adding french translation 2021-10-25 20:22:11 +02:00
ix 2dcea7cd74 update patreon link 2021-10-25 13:22:07 +02:00
Danhia ecd7ca69db first draft for cgu 2021-10-24 20:26:26 +02:00
ix e6e50fcd05 footer reworked, patreon link added, messages recompiled and cgu view added 2021-10-24 16:25:46 +02:00
ix 4dcc953baf fix solve list for event challenges, fix link color 2021-10-23 11:13:02 +02:00
ix a7620eec77 fix template profile 2021-10-22 16:09:05 +02:00
ix baa598f1aa fix template profile 2021-10-22 16:08:22 +02:00
ix ddccb96586 Merge branch 'events' into main 2021-10-22 14:21:16 +02:00
ix 9f0273a85b Merge branch 'main' of github.com:Danhia/42CTF into main 2021-10-22 14:21:12 +02:00
ix 63349df20f Update fix 2021-10-22 14:21:05 +02:00
Ix 1274a6b7fe
Merge pull request #1 from Danhia/events
Update fix
2021-10-22 13:59:27 +02:00
ix f5b54e3ed6 Merge branch 'main' of github.com:Danhia/42CTF into main 2021-10-22 13:58:40 +02:00
ix 0a42af4a9b Update fix 2021-10-22 13:58:23 +02:00
Ix 87b1a5f6f8
Update README.md 2021-10-22 11:48:28 +02:00
ix a379f4934d Update todo 2021-10-22 11:46:48 +02:00
ix 86cc6070e1 Events done 2021-10-22 11:45:50 +02:00
ix c98e89b5ca events added, need to smooth html and css 2021-10-21 15:02:02 +02:00
Ix 8d3cd60ae4
Update README.md 2021-09-21 21:42:55 +02:00
ix e956082385 category challenges listed by point decrease 2021-09-08 21:50:28 +02:00
ix 071ba34792 Discord button removed, css fix when 0 at category stat, messages recompiled 2021-09-08 21:36:15 +02:00
ix 81e283ea69 Categories stats added, css modified, fix responsive display of profile page 2021-09-08 16:53:00 +02:00
ix ed928f6f33 Merge branch 'graph' into main 2021-09-08 04:00:39 +02:00
ix ce31ce30ee Charts changed to highcharts 2021-09-08 03:59:35 +02:00
ix 500d6fe228 Charts added 2021-09-08 00:32:44 +02:00
Danhia 0c0506505b fixed missing translation header in password reset templates 2021-09-07 20:42:18 +00:00
ix 62b3b7e5cd internationalization for news 2021-09-07 21:13:30 +02:00
ix acbf38a6a7 Merge remote-tracking branch 'origin/traduction-fr' into main 2021-09-07 19:54:05 +02:00
ix dbdd6b06f8 internationalization fixed, templates url fixed, messages recompiled 2021-09-07 19:52:40 +02:00
Danhia 428edadb31 changed translation for sign up 2021-09-06 21:34:45 +02:00
Danhia 768e62f369 french translation 2021-09-06 21:04:42 +02:00
ix 52c2dcdec3 last 2021-09-06 20:15:03 +02:00
ix 42441e7854 local settings 2021-09-06 20:14:23 +02:00
ix b0cea92ea8 local settings 2021-09-06 20:12:03 +02:00
ix 0c285cc486 okay it's done i think 2021-09-06 19:59:13 +02:00
ix 5e0da0e8b3 okay it's done i think 2021-09-06 19:42:13 +02:00
Ix faec388ad5
Create settings.py 2021-07-10 12:34:42 +02:00
Ix afb572d091
Update .gitignore 2021-07-10 12:29:40 +02:00
253 changed files with 17421 additions and 5760 deletions

3
.gitignore vendored
View File

@ -1,3 +1,4 @@
local_settings.py
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
@ -108,7 +109,7 @@ media/
*.pid
*.json
settings.py
local-settings.py
/data
/config

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "src/ctfs/templates/challenges"]
path = src/ctfs/templates/challenges
url = https://gitea.42ctf.org/42CTF/challenges-descriptions.git

View File

@ -1,28 +1,34 @@
# 42ctf
CTF by 42 students
# 42CTF
[42CTF](https://www.42ctf.org) is a CTF platform created by School 42 students and open to anyone.
### Todo
- [x] Serveur SMTP & reset password
- [x] Clean le repo
- [x] Accès au chall après validation
- [x] Section "Intro"
- [x] Section Treasure Hunt
- [x] Edition de profil
- [ ] Ajouter de la Doc
- [x] Infrastructure de pwn
- [x] Organiser une session découverte
- [x] Compteur de flags
- [ ] Graphiques statistiques
- [x] Création d'un discord linkable
- [ ] Refonte du linkage discord -> 42ctf
- [ ] Traduction du site
- [x] Anglais
- [ ] Français
- [ ] Russe
- [ ] Espagnol
- [ ] Italien
- [ ] OAuth 42
- [ ] Feature proposer une solution à un challenge
- [ ] Système de badge/succès
- [ ] Génération d'une page résumant le profil d'un utilisateur (ex: show resume sur intra.42.fr)
TODO has been migrated to [issues](https://gitea.42ctf.org/42CTF/website/issues) !
And hopefully, it is not redirected anymore to `/dev/null`.
### How to contribute ?
First, you need to contact a 42CTF admin to get an account on the 42CTF gitea.
You can contact us on [discord](https://discord.gg/3KDvt6hbWW) or by [email](mailto:42ctf@protonmail.com).
You can also fill this [form](https://forms.42l.fr/apps/forms/bpmyGR37AR4yHGnC) and we'll contact you.
Then, once you have a gitea account, you can fork this repository, do some stuff, and open a pull request.
If you want to translate the platform, then have a look at the [wiki](https://gitea.42ctf.org/42CTF/website/wiki).
If you want to help with bot development, it has now its own [repository](https://gitea.42ctf.org/42CTF/bot)
### How to set up my dev environment ?
There is only one file missing on this repository for you to run the server: `local_settings.py`.
You should create one in the `src` directory, with the following content:
```
DEBUG = True
SECRET_KEY = 'what you want'
```
When you'll run `python manage.py migrate` then `python manage.py runserver`, an empty database will be automatically created.
The `local_settings.py` is in the `.gitignore` and should stay that way, so we don't accidentally overwrite the production file when we deploy.
To obtain administrator rights you can run `python manage.py createsuperuser`.

104
bot.py
View File

@ -1,104 +0,0 @@
import os
import discord
import discord.utils
import urllib.request, json
import asyncio
import json
import logging
TOKEN = os.getenv('DISCORD_TOKEN')
GUILD = '42ctf'
intents = discord.Intents.all()
client = discord.Client(intents=intents)
db_file = open('members.json', 'r')
users = json.load(db_file)
db_file.close()
logging.basicConfig(filename='bot.log', format='%(asctime)s %(message)s', level=logging.INFO)
guild = ''
roles = {}
def get_rank(token):
url = urllib.request.urlopen("https://www.42ctf.org/accounts/rank/" + token)
data = json.loads(url.read().decode())
rank = data['rank']
return rank
async def watch_roles():
global users
await client.wait_until_ready() # ensures cache is loaded
while not client.is_closed():
for member_id, token in users.items():
if (token == "0000"):
continue
member = discord.utils.get(guild.members, id=int(member_id))
rank = get_rank(token)
if rank == 1 and roles['top1'] not in member.roles:
await member.add_roles(roles['top1'])
await member.remove_roles(roles['top10'])
await member.remove_roles(roles['top50'])
elif rank > 1 and rank <= 10 and roles['top10'] not in member.roles:
await member.add_roles(roles['top10'])
await member.remove_roles(roles['top1'])
await member.remove_roles(roles['top50'])
elif rank > 10 and rank <= 50 and roles['top50'] not in member.roles:
await member.add_roles(roles['top50'])
await member.remove_roles(roles['top10'])
await member.remove_roles(roles['top1'])
elif rank > 50:
await member.remove_roles(roles['top1'])
await member.remove_roles(roles['top10'])
await member.remove_roles(roles['top50'])
await asyncio.sleep(60)
@client.event
async def on_ready():
global guild, roles
guild = discord.utils.get(client.guilds, name=GUILD)
roles['top10'] = discord.utils.get(guild.roles, id=801787467064672286)
roles['top1'] = discord.utils.get(guild.roles, id=798638767359524875)
roles['top50'] = discord.utils.get(guild.roles, id=803729539145924649)
logging.info('%s is connected to the following guild: %s(id: %d)', client.user, guild.name, guild.id)
client.loop.create_task(watch_roles())
@client.event
async def on_message(message):
global guild, roles
if message.author == client.user:
return
if '!connect' in message.content:
try:
user_token = message.content.split(' ')[1]
member = discord.utils.get(guild.members, name=message.author.name)
rank = get_rank(user_token)
users[str(member.id)] = user_token
logging.info("MESSAGE: from %s with token %s", message.author.name, user_token)
with open('members.json', 'w') as json_file:
json.dump(users, json_file)
if rank == 1:
await member.add_roles(roles['top1'])
response = "Congratulations, you're now Top 1. But for how long ?"
elif (rank <= 10):
await member.add_roles(roles['top10'])
response = "You've been granted the Top 10 role. Now, go away and flag !"
elif rank <= 50:
await member.add_roles(roles['top50'])
response = "You've been granted the Top 50 role. Now, go away and flag !"
else:
response = "No role for you now, but I'll keep watching you."
except IndexError:
response = 'usage: !connect 42ctf_token'
await message.author.create_dm()
await message.author.dm_channel.send(response)
client.run(TOKEN)

3
requirements.txt Normal file
View File

@ -0,0 +1,3 @@
Django==3.2.11
requests==2.27.1
authlib==0.15.5

23
src/TODO Normal file
View File

@ -0,0 +1,23 @@
[X] make relation between user and events
[X] make scoreboard for events
[X] make access mod for events :
[X] Sub button for public events
[X] Access by password
[X] Begin date for display challenges
[X] Ending date for stop flag submission
[X] Admin rights
[X] Admin can access to events pages without password
[X] Admin can subscribe to event without password
[X] process flag submission
[X] increment user score in Scores model
[X] add filters for admin dashboard
[X] add search in fields in admin dashboard
[X] display more information in admin dashboard
Front End :
[X] Smooth display of events listing
[X] Nice page with background, logo etc for event detail

View File

@ -1,5 +1,12 @@
from .models import UserProfileInfo
from django.contrib import admin
admin.site.register(UserProfileInfo)
#admin.site.register(UserProfileInfo)
# Register your models here.
@admin.register(UserProfileInfo)
class userprofile(admin.ModelAdmin):
#list display
list_display = ['user', 'score', 'last_submission_date']
# search list
search_fields = ['score', 'user__username']

View File

@ -0,0 +1,29 @@
# Generated by Django 3.1.5 on 2022-01-23 17:04
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('accounts', '0006_auto_20210608_2229'),
]
operations = [
migrations.AddField(
model_name='userprofileinfo',
name='member',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='userprofileinfo',
name='member_since',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='Member since'),
),
migrations.AddField(
model_name='userprofileinfo',
name='member_until',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='Member until'),
),
]

View File

@ -12,6 +12,9 @@ class UserProfileInfo(models.Model):
last_submission_date = models.DateTimeField('Last Submission Date', default=timezone.now)
token = models.CharField(max_length=200, blank=True)
discord_id = models.CharField(max_length=20, null=True, blank=True, unique=True)
member = models.BooleanField(default=False)
member_since = models.DateTimeField('Member since', default=timezone.now)
member_until = models.DateTimeField('Member until', default=timezone.now)
def __str__(self):
return self.user.username
class Meta:

View File

@ -0,0 +1,33 @@
{% extends 'base.html' %}
{% block content %}
{% load i18n %}
<div class="row">
<div class="col-sm-12 col-md-9">
<div class="ctf-block">
<div class="ctf-head">
<h3>{% trans "Delete account" %}</h3>
</div>
<div class="ctf-body">
{% trans "Please confirm your password to delete your account." %}<br>
{% trans "Deleted accounts cannot be recovered." %}<br><br>
<div class="col-sm-8 col-md-6 mx-auto">
{% if bad_password %}
<span class="message error-msg">{% trans "Password inccorect." %}</span>
{% elif deleted %}
<span class="message success-msg">{% trans "Your account has been deleted." %}</span>
{% endif %}
<form method="post">
{% csrf_token %}
<div class="form-group">
<input class="form-control" type="password" name="password" placeholder="{% trans "Password" %}"></br>
<input class="form-control" type="submit" value="Delete">
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -2,78 +2,89 @@
{% block content %}
{% load i18n %}
<div class="row">
<div class="col-sm-12 col-md-9">
<div class="ctf-block">
<div class="ctf-head">
<h3>Edit info</h3>
</div>
<div class="bloc-body">
<div class="col-sm-12 col-md-12 mx-auto">
{{ u_form.non_field_errors }}
{% if error is not None %}
<div class="col-sm-12 col-md-9">
<div class="ctf-block">
<div class="ctf-head">
<h3>Edit info</h3>
</div>
<div class="bloc-body">
<div class="col-sm-12 col-md-12 mx-auto">
{{ u_form.non_field_errors }}
{% if error is not None %}
<span class="message error-msg">{{ error }}</span>
{% elif success is not None %}
{% elif success is not None %}
<span class="message success-msg">{{ success }}</span>
{% endif %}
<form method='POST'>
<div class="edit-infos-grp">
{%csrf_token%}
<label for="{{ u_form.username.id_for_label }}">{% trans "Username" %} *</label>
{{ u_form.username.errors}}
{{u_form.username}}
</br>
<label for="{{ u_form.email.id_for_label }}">{% trans "Email" %} *</label>
{{ u_form.email.errors}}
{{u_form.email}}
</br>
</br>
<label for="{{ p_form.portfolio_site.id_for_label }}">{% trans "Website" %}</label>
{{p_form.portfolio_site}}
</br>
</br>Token
<input type='text' readonly value='{{token}}'>
</br>
<input class="form-control" type="submit" value="{% trans "Apply" %}">
</div>
</form>
{% endif %}
<form method='POST'>
<div class="edit-infos-grp">
{%csrf_token%}
<label for="{{ u_form.username.id_for_label }}">{% trans "Username" %} *</label>
{{ u_form.username.errors}}
{{u_form.username}}
</br>
<label for="{{ u_form.email.id_for_label }}">{% trans "Email" %} *</label>
{{ u_form.email.errors}}
{{u_form.email}}
</br>
</br>
<label for="{{ p_form.portfolio_site.id_for_label }}">{% trans "Website" %}</label>
{{p_form.portfolio_site}}
</br>
</br>Token
<input type='text' readonly value='{{token}}'>
</br>
<input class="form-control" type="submit" value="{% trans " Apply" %}">
</div>
</form>
</div>
</div>
</div>
</div>
<div class="ctf-block">
<div class="ctf-head">
<h3>{% trans "Connected accounts" %}</h3>
</div>
<div class="bloc-body">
<div class="d-flex">
{% if user.userprofileinfo.discord_id|length > 0 %}
<form action="{% url 'accounts:connections-disconnect-discord' %}" method='POST' class="form-inline p-2">
<div class="ctf-block">
<div class="ctf-head">
<h3>{% trans "Connected accounts" %}</h3>
</div>
<div class="bloc-body">
<div class="d-flex">
{% if user.userprofileinfo.discord_id|length > 0 %}
<form action="{% url 'accounts:connections-disconnect-discord' %}" method='POST'
class="form-inline p-2">
{%csrf_token%}
<button class="btn btn-dark" type="submit">{% trans "Disconnect Discord" %}</button>
</form>
{% else %}
<form action="{% url 'accounts:connections-connect-discord' %}" method='POST' class="form-inline p-2">
{% else %}
<form action="{% url 'accounts:connections-connect-discord' %}" method='POST'
class="form-inline p-2">
{%csrf_token%}
<button class="btn btn-dark" type="submit">{% trans "Connect Discord" %}</button>
</form>
{% endif %}
{% endif %}
</div>
</div>
</div>
</div>
</div>
<div class="d-none d-md-block col-10 col-md-3 right-sidebar">
<ul class="list-group">
<div class="d-none d-md-block col-10 col-md-3 right-sidebar">
<ul class="list-group">
<li class="list-group-item">{{ user.username }}</li>
<li class="list-group-item">{% trans "Score" %} : {{ user.userprofileinfo.score }}</li>
{% if user.userprofileinfo.portfolio_site %}
<li class="list-group-item">
<a href="{{ user.userprofileinfo.portfolio_site }}" target="_blank">
{{ user.userprofileinfo.portfolio_site }}
</a>
</li>
<li class="list-group-item">
<a href="{{ user.userprofileinfo.portfolio_site }}" target="_blank">
{{ user.userprofileinfo.portfolio_site }}
</a>
</li>
{% endif %}
<li class="list-group-item">{% trans "Member since" %} {{ user.date_joined|date:"Y-m-d" }}</li>
<li class="list-group-item">{% trans "Registered since" %} {{ user.date_joined|date:"Y-m-d" }}</li>
</ul>
<ul class="list-group">
<form method='GET' action="{% url 'accounts:delete_account' %}">
{%csrf_token%}
<li class="list-group-item">
<input class="form-control" type="submit" value="{% trans " Delete my account" %}">
</li>
</form>
</ul>
</div>
</div>
{% endblock %}
</div>
{% endblock %}

View File

@ -8,28 +8,28 @@
<h3>Login</h3>
</div>
<div class="ctf-body">
<div class="col-sm-8 col-md-6 mx-auto">
{% if error %}
<span class="message error-msg">{% trans "Please, verify your infos." %}</span>
{% endif %}
<form method="post">
{% csrf_token %}
<div class="form-group">
<input class="form-control" type="text" name="username" placeholder="{% trans "Username" %}"></br>
<input class="form-control" type="password" name="password" placeholder="{% trans "Password" %}"></br>
<input class="form-control" type="submit" value="login">
</div>
<a href="{% url 'password_reset' %}">{% trans "Reset password" %}</a>
</form>
</div>
<div class="col-sm-8 col-md-6 mx-auto">
{% if error %}
<span class="message error-msg">{% trans "Please, verify your infos." %}</span>
{% endif %}
<form method="post">
{% csrf_token %}
<div class="form-group">
<input class="form-control" type="text" name="username" placeholder="{% trans "Username" %}"></br>
<input class="form-control" type="password" name="password" placeholder="{% trans "Password" %}"></br>
<input class="form-control" type="submit" value="login">
</div>
<a href="{% url 'password_reset' %}">{% trans "Reset password" %}</a>
</form>
</div>
</div>
</div>
</div>
<div class="col-sm-12 col-md-3 right-sidebar">
<ul class="list-group">
<a href="/accounts/signin" class="list-group-item">{% trans "Login" %}</a>
<a href="/accounts/signup" class="list-group-item">{% trans "Sign up" %}</a>
<a href="/accounts/signin" class="list-group-item">{% trans "Login" %}</a>
<a href="/accounts/signup" class="list-group-item">{% trans "Sign up" %}</a>
</ul>
</div>
</div>

View File

@ -1,13 +1,22 @@
{% extends 'base.html' %}
{% block content %}
{% load i18n %}
{% load key_value %}
{% load is_member %}
{% ismember user.userprofileinfo as is_member %}
<div class="row">
<div class="col-sm-12 col-md-9">
<div>
<h4>Challenges Solved by {{ user.username }}</h4>
{% if solves%}
<table class="table table-dark">
<thead>
<h4>{% trans "Challenges Solved by" %} <span class="{{ is_member }}">{{ user.username }}</span></h4>
{% if solves%}
<div class="table table-dark">
<div class="card-body">
<div id="time-chart"></div>
</div>
</div>
<table class="table table-dark">
<thead>
<tr>
<th scope="col">{% trans "Challenge Name" %}</th>
<th scope="col">{% trans "Category" %}</th>
@ -18,7 +27,7 @@
<tbody>
{% for s in solves %}
<tr>
<th scope="row"><a href="/ctfs/{{ s.ctf.category.slug }}/{{ s.ctf.slug }}">{{ s.ctf.name }}</a></th>
<th scope="row"><a href="{% url 'ctf' cat_slug=s.ctf.category.slug ctf_slug=s.ctf.slug %}">{{ s.ctf.name }}</a></th>
<td>{{ s.ctf.category.name}}</td>
<td>{{ s.ctf.points }}</td>
<td>{{ s.flag_date|date:"Y-m-d H:i:s" }}</td>
@ -27,14 +36,15 @@
</tbody>
</table>
{% else %}
<p>{% trans "It's seem {{ user.username }} have never solved any CTF yet..." %}</p>
<p>{% trans "It seems that this user has not solved any challenge yet..." %}</p>
{% endif %}
</div>
</div>
<div class="d-none d-md-block col-10 col-md-3 right-sidebar">
<ul class="list-group">
<li class="list-group-item">{{ user.username }}</li>
<li class="list-group-item">{% trans "Score" %} : {{ user.userprofileinfo.score }}</li>
<li class="list-group-item {{ is_member }}">{{ user.username }}</li>
<li class="list-group-item">{% trans "Score" %} : {{ score }}</li>
<li class="list-group-item">{% trans "Rank" %} : {{ rank }}</li>
{% if user.userprofileinfo.portfolio_site %}
<li class="list-group-item">
<a href="{{ user.userprofileinfo.portfolio_site }}" target="_blank">
@ -42,9 +52,101 @@
</a>
</li>
{% endif %}
<li class="list-group-item">{% trans "Member since" %} {{ user.date_joined|date:"Y-m-d" }}</li>
{% if member %}
<li class="list-group-item is-member">{% trans "Status: Member" %}</li>
{% else %}
<li class="list-group-item">{% trans "Status: Visitor" %}</li>
{% endif %}
<li class="list-group-item">{% trans "Registered since" %} {{ user.date_joined|date:"d-m-Y" }}</li>
</ul>
<ul class="list-group">
<li class="list-group-item">{% trans "Categories stats" %}</li>
{% for cat in catsDatas %}
<li class="list-group-item" style="padding-bottom: 3;padding-top: 0;">
<span>{{ cat.0 }}</span>
<div class="progress">
{% if cat.3 == '0' %}
<div class="progress-bar bg-success" role="progressbar" style="width: 0%;color:#d9d9d9;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">0 %</div>
{% else %}
<div class="progress-bar bg-success" role="progressbar" style="width: {{ cat.3 }}%" aria-valuenow="{{ cat.3 }}" aria-valuemin="0" aria-valuemax="100">{{ cat.3 }} %</div>
{% endif %}
</div>
</li>
{% endfor %}
</ul>
</div>
</div>
<script src="https://code.highcharts.com/highcharts.src.js"></script>
<script>
Highcharts.theme={colors:["#2b908f","#90ee7e","#f45b5b","#7798BF","#aaeeee","#ff0066","#eeaaee","#55BF3B","#DF5353","#7798BF","#aaeeee"],chart:{backgroundColor:{linearGradient:{x1:0,y1:0,x2:1,y2:1},stops:[[0,"#1D1D1D"],[1,"#1D1D1D"]]},style:{fontFamily:"'Unica One', sans-serif"},plotBorderColor:"#606063"},title:{style:{color:"#E0E0E3",textTransform:"uppercase",fontSize:"20px"}},subtitle:{style:{color:"#E0E0E3",textTransform:"uppercase"}},xAxis:{gridLineColor:"#707073",labels:{style:{color:"#E0E0E3"}},lineColor:"#707073",minorGridLineColor:"#505053",tickColor:"#707073",title:{style:{color:"#A0A0A3"}}},yAxis:{gridLineColor:"#707073",labels:{style:{color:"#E0E0E3"}},lineColor:"#707073",minorGridLineColor:"#505053",tickColor:"#707073",tickWidth:1,title:{style:{color:"#A0A0A3"}}},tooltip:{backgroundColor:"rgba(0, 0, 0, 0.85)",style:{color:"#F0F0F0"}},plotOptions:{series:{dataLabels:{color:"#F0F0F3",style:{fontSize:"13px"}},marker:{lineColor:"#333"}},boxplot:{fillColor:"#505053"},candlestick:{lineColor:"white"},errorbar:{color:"white"}},legend:{backgroundColor:"#1D1D1D",itemStyle:{color:"#E0E0E3"},itemHoverStyle:{color:"#FFF"},itemHiddenStyle:{color:"#606063"},title:{style:{color:"#C0C0C0"}}},credits:{style:{color:"#666"}},labels:{style:{color:"#707073"}},drilldown:{activeAxisLabelStyle:{color:"#F0F0F3"},activeDataLabelStyle:{color:"#F0F0F3"}},navigation:{buttonOptions:{symbolStroke:"#DDDDDD",theme:{fill:"#505053"}}},rangeSelector:{buttonTheme:{fill:"#505053",stroke:"#000000",style:{color:"#CCC"},states:{hover:{fill:"#707073",stroke:"#000000",style:{color:"white"}},select:{fill:"#000003",stroke:"#000000",style:{color:"white"}}}},inputBoxBorderColor:"#505053",inputStyle:{backgroundColor:"#333",color:"silver"},labelStyle:{color:"silver"}},navigator:{handles:{backgroundColor:"#666",borderColor:"#AAA"},outlineColor:"#CCC",maskFill:"rgba(255,255,255,0.1)",series:{color:"#7798BF",lineColor:"#A6C7ED"},xAxis:{gridLineColor:"#505053"}},scrollbar:{barBackgroundColor:"#808083",barBorderColor:"#808083",buttonArrowColor:"#CCC",buttonBackgroundColor:"#606063",buttonBorderColor:"#606063",rifleColor:"#FFF",trackBackgroundColor:"#404043",trackBorderColor:"#404043"}};
Highcharts.setOptions(Highcharts.theme);
Highcharts.chart('time-chart', {
title: {
text: 'Points earned for each category'
},
yAxis: {
title: {
text: 'Points earned'
}
},
xAxis: {
type: 'datetime',
labels: {
formatter: function() {
return Highcharts.dateFormat('%d.%b %Y',
this.value);
}
}
},
legend: {
layout: 'vertical',
align: 'right',
verticalAlign: 'middle'
},
plotOptions: {
pointStart: {{ user.date_joined|timestamp_fromdate }},
series: {
label: {
connectorAllowed: false
},
allowPointSelect: true,
marker: {
enabled: true
}
}
},
series: [
{
name: 'Total',
data: {{ solved|safe }}
},
{% for cat in cats %}
{
name: '{{ cat.name }}',
data: {{ pointDatas|keyvalue:cat.name|safe }},
visible: false,
},
{% endfor %}
],
responsive: {
rules: [{
condition: {
maxWidth: 500
},
chartOptions: {
legend: {
layout: 'horizontal',
align: 'center',
verticalAlign: 'bottom'
}
}
}]
}
});
</script>
{% endblock %}

View File

View File

@ -0,0 +1,11 @@
from django import template
register = template.Library()
@register.filter
def keyvalue(dict, key):
return dict[key]
@register.filter
def timestamp_fromdate(date):
return str(date.timestamp() * 1000).replace(',','.')

View File

@ -12,5 +12,6 @@ urlpatterns = [
path('rank/<str:token>', views.rank, name='rank'),
path('connections/connect/discord', views.connection.connect, name='connections-connect-discord'),
path('connections/connect/discord/authorize', views.connection.authorize, name='connections-connect-discord-authorize'),
path('connections/disconnect/discord', views.connection.disconnect, name='connections-disconnect-discord')
path('connections/disconnect/discord', views.connection.disconnect, name='connections-disconnect-discord'),
path('delete_account/', views.delete_account, name='delete_account'),
]

View File

@ -4,6 +4,7 @@ from django.views.decorators.http import require_POST
from django.views.defaults import bad_request
from django.urls import reverse
from django.shortcuts import redirect
from django.contrib.sites.models import Site
import os
oauth = OAuth()
@ -23,8 +24,9 @@ oauth.register(
def connect(request):
if request.user.userprofileinfo.discord_id:
return bad_request(request, "Already connected")
site = Site.objects.get_current()
redirect_uri = reverse('accounts:connections-connect-discord-authorize')
redirect_uri = request.build_absolute_uri(redirect_uri)
redirect_uri = "https://" + site.domain + redirect_uri[3:] # remove language code
print(redirect_uri)
return oauth.discord.authorize_redirect(request, redirect_uri)

View File

@ -2,7 +2,7 @@ from django.shortcuts import render, redirect, get_object_or_404
from django.utils.translation import gettext_lazy as _
from django import forms
from ctfs.models import CTF_flags
from ctfs.models import Category, CTF_flags, CTF
from ..forms import UserForm,UserProfileInfoForm, UserInfosUpdateForm, UserUpdateForm
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.models import User
@ -15,116 +15,171 @@ from django.urls import reverse
from secrets import token_hex
from accounts.models import UserProfileInfo
from django.contrib.auth.models import timezone
import os
from . import connection
def signin(request):
if not request.user.is_authenticated:
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
user = authenticate(username=username, password=password)
if user:
if user.is_active:
login(request,user)
return HttpResponseRedirect(reverse('home'))
else:
return HttpResponse(_("Your account was inactive."))
else:
return render(request, 'accounts/login.html', {'error': True})
else:
return render(request, 'accounts/login.html', {})
else:
return HttpResponseRedirect(reverse('home'))
if not request.user.is_authenticated:
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
user = authenticate(username=username, password=password)
if user:
if user.is_active:
login(request,user)
return HttpResponseRedirect(reverse('home'))
else:
return HttpResponse(_("Your account was inactive."))
else:
return render(request, 'accounts/login.html', {'error': True})
else:
return render(request, 'accounts/login.html', {})
else:
return HttpResponseRedirect(reverse('home'))
def signup(request):
if not request.user.is_authenticated:
user_form = UserForm()
profile_form = UserProfileInfoForm()
registered = False
if request.method == 'POST':
pass1 = request.POST.get('password')
if len(pass1) < 8:
return render(request,'accounts/register.html', {'user_form':user_form, 'profile_form':profile_form, 'registered_failed':"The new password must be at least %d characters long." % 8})
first_isalpha = pass1[0].isalpha()
if not any(c.isdigit() for c in pass1) or not any(c.isalpha() for c in pass1):
return render(request,'accounts/register.html', {'user_form':user_form, 'profile_form':profile_form, 'registered_failed':_("The password must contain at least one letter and at least one digit or punctuation character.")})
if User.objects.filter(email=request.POST.get('email')).exists():
return render(request,'accounts/register.html', {'user_form':user_form, 'profile_form':profile_form, 'registered_failed':_("A user with that email already exists.")})
user_form = UserForm(data=request.POST)
profile_form = UserProfileInfoForm(data=request.POST)
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save()
user.set_password(user.password)
user.save()
profile = profile_form.save(commit=False)
profile.user = user
profile.token = token_hex(16)
profile.save()
registered = True
else:
return render(request,'accounts/register.html', {'user_form':user_form, 'profile_form':profile_form, 'registered_failed':_("A user with that username already exists.")})
return render(request,'accounts/register.html',
{'user_form':user_form,
'profile_form':profile_form,
'registered':registered})
else:
return HttpResponseRedirect(reverse('home'))
if not request.user.is_authenticated:
user_form = UserForm()
profile_form = UserProfileInfoForm()
registered = False
if request.method == 'POST':
pass1 = request.POST.get('password')
if len(pass1) < 8:
return render(request,'accounts/register.html', {'user_form':user_form, 'profile_form':profile_form, 'registered_failed':"The new password must be at least %d characters long." % 8})
first_isalpha = pass1[0].isalpha()
if not any(c.isdigit() for c in pass1) or not any(c.isalpha() for c in pass1):
return render(request,'accounts/register.html', {'user_form':user_form, 'profile_form':profile_form, 'registered_failed':_("The password must contain at least one letter and at least one digit or punctuation character.")})
if User.objects.filter(email=request.POST.get('email')).exists():
return render(request,'accounts/register.html', {'user_form':user_form, 'profile_form':profile_form, 'registered_failed':_("A user with that email already exists.")})
user_form = UserForm(data=request.POST)
profile_form = UserProfileInfoForm(data=request.POST)
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save()
user.set_password(user.password)
user.save()
profile = profile_form.save(commit=False)
profile.user = user
profile.token = token_hex(16)
profile.save()
registered = True
else:
return render(request,'accounts/register.html', {'user_form':user_form, 'profile_form':profile_form, 'registered_failed':_("A user with that username already exists.")})
return render(request,'accounts/register.html',
{'user_form':user_form,
'profile_form':profile_form,
'registered':registered})
else:
return HttpResponseRedirect(reverse('home'))
@login_required
def out(request):
logout(request)
return HttpResponseRedirect(reverse('home'))
logout(request)
return HttpResponseRedirect(reverse('home'))
@login_required
def edit(request):
if request.method == 'POST':
umail = request.user.email
uuser = request.user.username
p_form = UserInfosUpdateForm(request.POST, instance=request.user.userprofileinfo)
u_form = UserUpdateForm(request.POST, instance=request.user)
error = None
success = None
if p_form.is_valid() and u_form.is_valid():
pmail = u_form.cleaned_data['email']
if pmail == umail:
pass
else:
if User.objects.filter(email=pmail).exists():
error = _("Email already taken.")
puser = u_form.cleaned_data['username']
if puser == uuser:
pass
else:
if User.objects.filter(username=puser).exists():
error = _("Username already taken.")
if error is None:
u_form.save()
p_form.save()
success = _("Updated.")
request.user.username = uuser
context={'p_form': p_form, 'u_form': u_form, 'error':error, 'success' : success}
return render(request, 'accounts/edit.html', context)
else:
p_form = UserInfosUpdateForm(instance=request.user.userprofileinfo)
u_form = UserUpdateForm(instance=request.user)
context={'p_form': p_form, 'u_form': u_form, 'token': request.user.userprofileinfo.token}
return render(request, 'accounts/edit.html',context )
if request.method == 'POST':
umail = request.user.email
uuser = request.user.username
p_form = UserInfosUpdateForm(request.POST, instance=request.user.userprofileinfo)
u_form = UserUpdateForm(request.POST, instance=request.user)
error = None
success = None
if p_form.is_valid() and u_form.is_valid():
pmail = u_form.cleaned_data['email']
if pmail == umail:
pass
else:
if User.objects.filter(email=pmail).exists():
error = _("Email already taken.")
puser = u_form.cleaned_data['username']
if puser == uuser:
pass
else:
if User.objects.filter(username=puser).exists():
error = _("Username already taken.")
if error is None:
u_form.save()
p_form.save()
success = _("Updated.")
request.user.username = uuser
context={'p_form': p_form, 'u_form': u_form, 'error':error, 'success' : success}
return render(request, 'accounts/edit.html', context)
else:
p_form = UserInfosUpdateForm(instance=request.user.userprofileinfo)
u_form = UserUpdateForm(instance=request.user)
context={'p_form': p_form, 'u_form': u_form, 'token': request.user.userprofileinfo.token}
return render(request, 'accounts/edit.html',context )
@login_required
def profile(request, user_name):
user_obj = get_object_or_404(User, username=user_name)
solves = CTF_flags.objects.filter(user=user_obj).order_by('-flag_date')
return render(request,'accounts/profile.html', {'user':user_obj, 'solves':solves})
# Create your views here.
catsDatas = []
user_obj = get_object_or_404(User, username=user_name)
all_users = list(UserProfileInfo.objects.select_related().order_by('-score', 'last_submission_date', 'user__username'))
rank = all_users.index(get_object_or_404(UserProfileInfo, user=user_obj)) + 1
if (user_obj.userprofileinfo.member and user_obj.userprofileinfo.member_until > timezone.now()):
member = True
else:
member = False
all_cats = Category.objects.all()
cats = [cat for cat in all_cats if CTF.objects.filter(category__name=cat.name, event=None, disabled=False)]
pointDatas = {}
for cat in cats:
# prepare categories
solved = CTF_flags.objects.filter(user=user_obj, ctf__category__name=cat.name, ctf__event=None, ctf__disabled=False).order_by('flag_date')
max_count = CTF.objects.filter(category__name=cat.name, event=None, disabled=False).count()
# get datas
somme = 0
solved_count = len(solved)
pointDatas[cat.name] = []
pointDatas[cat.name].append([user_obj.date_joined.timestamp() * 1000, 0])
percent = (solved_count / max_count) * 100
catsDatas.append([cat.name, solved_count, max_count, '{:.0f}'.format(percent)])
for flag in solved:
somme += flag.ctf.points
pointDatas[cat.name].append([flag.flag_date.timestamp() * 1000, somme])
solves = CTF_flags.objects.filter(user=user_obj, ctf__event=None, ctf__disabled=False).order_by('-flag_date')
solved = []
somme = 0
solved.append([user_obj.date_joined.timestamp() * 1000, 0])
for s in solves.reverse():
somme += s.ctf.points
solved.append([s.flag_date.timestamp() * 1000,somme])
return render(request,'accounts/profile.html', {'user':user_obj, 'solves':solves,'solved':solved,'catsDatas': catsDatas, 'pointDatas': pointDatas,
'rank': rank, 'score' : somme, 'member' : member, 'cats':cats})
@login_required
def delete_account(request):
if request.method == 'POST':
user = request.user
password = request.POST.get('password')
if user.check_password(password):
logout(request)
user.delete()
return render(request, 'accounts/delete.html', {'deleted': True, 'bad_password': False})
else:
return render(request, 'accounts/delete.html', {'deleted': False, 'bad_password': True})
else:
return render(request, 'accounts/delete.html', {'deleted': False, 'bad_password': False} )
def rank(request, token):
all_users = UserProfileInfo.objects.filter(score__gt=0).select_related().order_by('-score', 'last_submission_date', 'user__username')
rank = 1
for elem in all_users:
if elem.token == token:
break
rank += 1
data = {"rank": rank}
return JsonResponse(data)
all_users = UserProfileInfo.objects.filter(score__gt=0).select_related().order_by('-score', 'last_submission_date', 'user__username')
rank = 1
for elem in all_users:
if elem.token == token:
break
rank += 1
data = {"rank": rank}
return JsonResponse(data)

0
src/api/__init__.py Normal file
View File

3
src/api/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
src/api/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class ApiConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'api'

View File

3
src/api/models.py Normal file
View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

3
src/api/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

6
src/api/urls.py Normal file
View File

@ -0,0 +1,6 @@
from django.urls import path
from . import views
urlpatterns = [
path('bot/discord', views.discord_bot, name='discord_bot'),
]

27
src/api/views.py Normal file
View File

@ -0,0 +1,27 @@
from django.shortcuts import render
from accounts.models import UserProfileInfo
from django.http import JsonResponse
import os
# Create your views here.
def discord_bot(request):
if request.method != 'GET':
return JsonResponse({'error':'bad request'})
token = request.GET.get('token')
auth_token = os.getenv('BOT_TOKEN')
if (token != auth_token or not auth_token):
return JsonResponse({'error':'not authorized'})
all_users = UserProfileInfo.objects.select_related().order_by('-score', 'last_submission_date', 'user__username')
data = {}
rank = 1
for user in all_users:
if user.discord_id:
data[user.discord_id] = rank
rank += 1
return JsonResponse(data)

26
src/change_lang.py Normal file
View File

@ -0,0 +1,26 @@
from django.template import Library
from django.core.urlresolvers import resolve, reverse
from django.utils.translation import activate, get_language
register = Library()
@register.simple_tag(takes_context=True)
def change_lang(context, lang=None, *args, **kwargs):
"""
Get active page's url by a specified language
Usage: {% change_lang 'en' %}
"""
path = context['request'].path
url_parts = resolve( path )
url = path
cur_language = get_language()
try:
activate(lang)
url = reverse( url_parts.view_name, kwargs=url_parts.kwargs )
finally:
activate(cur_language)
return "%s" % url

View File

@ -2,7 +2,24 @@ from django.contrib import admin
from .models import Category, CTF, CTF_flags
admin.site.register(Category)
admin.site.register(CTF)
admin.site.register(CTF_flags)
#admin.site.register(CTF)
#admin.site.register(CTF_flags)
@admin.register(CTF_flags)
class ctf_flags(admin.ModelAdmin):
#list display
list_display = ['user', 'ctf', 'flag_date']
#list Filter
list_filter = ('ctf__category', 'ctf', 'user','flag_date')
# search list
search_fields = ['ctf__category__name', 'ctf__name', 'user__username']
@admin.register(CTF)
class ctf(admin.ModelAdmin):
#list display
list_display = ['name', 'event', 'category']
#list Filter
list_filter = ('category', 'event')
# search list
search_fields = ['category__name', 'name', 'author__username']
# Register your models here.

View File

View File

View File

@ -0,0 +1,28 @@
from collections import defaultdict
from django.core.management.base import BaseCommand, CommandError
from accounts.models import UserProfileInfo
from ctfs.models import CTF_flags, CTF
from math import log
class Command(BaseCommand):
help = 'Actualize challenges points based on number of solves'
def handle(self, *args, **options):
challenges = CTF.objects.filter(event=None, disabled=False).exclude(category__name="-Intro-")
for ctf in challenges:
solves = CTF_flags.objects.filter(ctf=ctf)
nb_solves = len(solves)
if nb_solves > 0:
new_points = max(200 - int(log(nb_solves)*8.5)*5, 5)
else:
new_points = 200
if new_points != ctf.points:
diff = ctf.points - new_points
ctf.points = new_points
ctf.save()
for s in solves:
s.user.userprofileinfo.score -= diff
s.user.userprofileinfo.save()

View File

@ -0,0 +1,28 @@
# Generated by Django 3.2.7 on 2021-09-07 14:07
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('ctfs', '0003_auto_20191003_1947'),
]
operations = [
migrations.AddField(
model_name='ctf',
name='description_de',
field=models.TextField(blank=True),
),
migrations.AddField(
model_name='ctf',
name='description_en',
field=models.TextField(blank=True),
),
migrations.AddField(
model_name='ctf',
name='description_ru',
field=models.TextField(blank=True),
),
]

View File

@ -0,0 +1,20 @@
# Generated by Django 3.2.7 on 2021-10-18 14:23
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('events', '0001_initial'),
('ctfs', '0004_auto_20210907_1407'),
]
operations = [
migrations.AddField(
model_name='ctf',
name='event',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='events.event'),
),
]

View File

@ -0,0 +1,20 @@
# Generated by Django 3.2.7 on 2021-10-19 15:03
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('events', '0002_alter_event_password'),
('ctfs', '0005_ctf_event'),
]
operations = [
migrations.AlterField(
model_name='ctf',
name='event',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='events.event'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.1.5 on 2022-02-03 17:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('ctfs', '0006_alter_ctf_event'),
]
operations = [
migrations.AddField(
model_name='ctf',
name='disabled',
field=models.BooleanField(default=False),
),
]

View File

@ -0,0 +1,26 @@
# Generated by Django 3.2.11 on 2022-02-15 16:13
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('ctfs', '0007_ctf_disabled'),
]
operations = [
migrations.RemoveField(
model_name='ctf',
name='description_de',
),
migrations.RemoveField(
model_name='ctf',
name='description_ru',
),
migrations.AddField(
model_name='ctf',
name='port',
field=models.PositiveSmallIntegerField(blank=True, null=True),
),
]

View File

@ -1,5 +1,6 @@
from django.db import models
from django.contrib.auth.models import User
from events.models import Event
class Category(models.Model):
name = models.CharField(max_length=200)
@ -11,9 +12,13 @@ class Category(models.Model):
class CTF(models.Model):
name = models.CharField(max_length=200)
flag = models.CharField(max_length=100)
disabled = models.BooleanField(default=False)
description = models.TextField(blank=True)
description_en = models.TextField(blank=True)
file = models.FileField(blank=True, upload_to='challenges')
ctf_url = models.URLField(blank=True)
port = models.PositiveSmallIntegerField(null=True, blank=True)
event = models.ForeignKey(Event, null=True, blank=True, on_delete=models.CASCADE)
points = models.PositiveSmallIntegerField()
slug = models.SlugField(max_length=55)
pub_date = models.DateTimeField('Date published')
@ -22,7 +27,7 @@ class CTF(models.Model):
on_delete=models.SET_NULL,
null=True,
)
author = models.ForeignKey(User, unique=False, on_delete=models.CASCADE)
author = models.ForeignKey(User, unique=False, on_delete=models.CASCADE)
def solved_by(self, user):
"""True if the exercise has been solved by the user."""
if not user.is_authenticated:

@ -0,0 +1 @@
Subproject commit 18fac3978d21dc824bcffa2bc960aa2bf6b4abd9

View File

@ -1,80 +1,95 @@
{% extends 'base.html' %}
{% block content %}
{% load i18n %}
{% load is_member %}
{% load get_chall %}
{% get_current_language as lang %}
<div class="row">
<div class="col-sm-12 col-md-9">
<div class="ctf-block">
<div class="ctf-head">
<h3>{{ ctf.name }}</h3>
<small>{% trans "Published date" %} : {{ ctf.pub_date }}</small>
<small>{% trans "Published date" %} : {{ ctf.pub_date }}</small>
</div>
{% if date < ctf.pub_date %}
<div class="ctf-body">
{{ ctf.description|safe }}
{% trans "Challenge is not yet available." %}
</div>
{% else %}
<div class="ctf-body">
{% get_chall_by_lang ctf lang as content %}
{{ content | safe }}
<!-- {% if ctf.port %}
<b>nc challenges.42ctf.org {{ ctf.port }}</b>
{% endif %} -->
</div>
<div class="ctf-footer">
{% if request.user.is_authenticated %}
{% if valitated == True %}
<p>{% trans "Congratulation !" %}</p>
{% elif alvalitated == True %}
<p>{% trans "Already flagged" %}</p>
{% if ctf.ctf_url %}
<a class="begin-ctf-link" target="_blank" href="{{ ctf.ctf_url }}">{% trans "Start the challenge" %}</a></br>
{% elif ctf.file %}
<a class="begin-ctf-link" target="_blank" href="{{ ctf.file.url }}">{% trans "Download" %}</a></br>
{% endif %}
{% else %}
{% if failed %}
<p>{% trans "Wrong flag ! You can do it !" %}</p>
{% endif %}
{% if ctf.ctf_url %}
<a class="begin-ctf-link" target="_blank" href="{{ ctf.ctf_url }}">{% trans "Start the challenge" %}</a></br>
{% elif ctf.file %}
<a class="begin-ctf-link" target="_blank" href="{{ ctf.file.url }}">{% trans "Download" %}</a></br>
{% endif %}
<form method="post" class="submitflag-form">
{% csrf_token %}
<input type="text" name="flag" maxlength="48" required="" id="id_flag">
<input class="form-control" type="submit" value=">">
</form>
{% endif %}
{% endif %}
{% if request.user.is_authenticated %}
{% if valitated == True %}
<p>{% trans "Congratulation !" %}</p>
{% elif alvalitated == True %}
<p>{% trans "Already flagged" %}</p>
{% if ctf.ctf_url %}
<a class="begin-ctf-link" target="_blank" href="{{ ctf.ctf_url }}">{% trans "Start the challenge" %}</a></br>
{% elif ctf.file %}
<a class="begin-ctf-link" target="_blank" href="{{ ctf.file.url }}">{% trans "Download" %}</a></br>
{% endif %}
{% else %}
{% if failed %}
<p>{% trans "Wrong flag ! You can do it !" %}</p>
{% endif %}
{% if ctf.ctf_url %}
<a class="begin-ctf-link" target="_blank" href="{{ ctf.ctf_url }}">{% trans "Start the challenge" %}</a></br>
{% elif ctf.file %}
<a class="begin-ctf-link" target="_blank" href="{{ ctf.file.url }}">{% trans "Download" %}</a></br>
{% endif %}
<form method="post" class="submitflag-form">
{% csrf_token %}
<input type="text" name="flag" maxlength="100" required="" id="id_flag">
<input class="form-control" type="submit" value=">">
</form>
{% endif %}
{% endif %}
</div>
{% endif %}
</div>
<div>
<h4>{% trans "Solved by" %}</h4>
{% if solved_list %}
<table class="table table-dark">
<h4>{% trans "Solved by" %}</h4>
{% if solved_list %}
<table class="table table-dark">
<thead>
<tr>
<th scope="col">{% trans "Username" %}</th>
<th scope="col">{% trans "Website" %}</th>
<th scope="col">{% trans "Score" %}</th>
<th scope="col">{% trans "Date" %}</th>
<th scope="col">{% trans "Username" %}</th>
<th scope="col">{% trans "Website" %}</th>
<th scope="col">{% trans "Score" %}</th>
<th scope="col">{% trans "Date" %}</th>
</tr>
</thead>
<tbody>
{% for s in solved_list %}
<tr>
<th scope="row"><a class="profile_link" href="/accounts/profile/{{ s.user.username }}"> {{ s.user.username }}</a></th>
<td>{{ s.user.userprofileinfo.portfolio_site }}</td>
<td>{{ s.user.userprofileinfo.score }}</td>
<td>{{ s.flag_date }}</td>
{% for s in solved_list %}
<tr>
{% ismember s.user.userprofileinfo as is_member %}
<th scope="row"><a class="profile_link {{is_member}}" href="{% url 'accounts:profile' user_name=s.user.username %}"> {{ s.user.username }}</a></th>
<td>{{ s.user.userprofileinfo.portfolio_site }}</td>
<td>{{ s.user.userprofileinfo.score }}</td>
<td>{{ s.flag_date }}</td>
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>
{% else %}
<p>{% trans "Nobody have solved this CTF." %}</p>
{% endif %}
{% else %}
<p>{% trans "Nobody has solved this challenge yet." %}</p>
{% endif %}
</div>
</div>
<div class="d-none d-md-block col-10 col-md-3 right-sidebar">
<ul class="list-group">
<li class="list-group-item">{% trans "Author" %} : {{ ctf.author.username }}</li>
<li class="list-group-item">{% trans "Point reward" %} : {{ ctf.points }}</li>
</ul>
{% ismember ctf.author.userprofileinfo as is_member %}
<li class="list-group-item">{% trans "Author" %} : <a style="position:absolute;right: 15px;" class="profile_link {{is_member}}" href="{% url 'accounts:profile' user_name=ctf.author.username %}">{{ ctf.author.username }}</a></li>
<li class="list-group-item">{% trans "Point reward" %} : <span style="position:absolute;right: 15px;">{{ ctf.points }}</span></li>
</ul>
</div>
</div>
{% endblock %}

4
src/ctfs/templates/ctfs/ctfs_list.html Normal file → Executable file
View File

@ -26,7 +26,7 @@
{% else %}
<th scope="row"> </th>
{% endif %}
<td><a href="/ctfs/{{ cat.slug }}/{{ ctf.slug }}">{{ ctf.name }}</td>
<td><a href="{% url 'ctf' cat_slug=ctf.category.slug ctf_slug=ctf.slug %}">{{ ctf.name }}</td>
<td>{{ ctf.points }}</td>
<td>{{ ctf.solved_num }}</td>
</tr>
@ -42,7 +42,7 @@
<li class="list-group-item active">{% trans "Categories" %}</li>
{% if cats %}
{% for c in cats %}
<a class="list-group-item" href="/ctfs/{{ c.slug }}">{{ c.name }}</a>
<a class="list-group-item" href="{% url 'category' cat_slug=c.slug %}">{{ c.name }}</a>
{% endfor %}
{% else %}
<li class="list-group-item">{% trans "No category available." %}</li>

View File

View File

@ -0,0 +1,14 @@
from django import template
register = template.Library()
@register.simple_tag
def get_chall_by_lang(chall, lang):
print(chall.slug)
filepath = "ctfs/templates/challenges/"+ lang + "/" + chall.slug + ".html"
print(filepath)
try:
with open(filepath) as fp:
return fp.read()
except:
return chall.description_en

View File

@ -4,19 +4,53 @@ from django.contrib.auth.models import timezone
from .models import Category, CTF, CTF_flags
from .forms import submit_flag
from accounts.models import UserProfileInfo
from django.utils.translation import get_language
from math import log
from accounts.models import UserProfileInfo
def get_description_by_lang(ctf):
lang = get_language()
ret = None
if lang == "fr":
ret = ctf.description
elif lang == "en":
ret = ctf.description_en
elif lang == "de":
ret = ctf.description_de
elif lang == "ru":
ret = ctf.description_ru
return ret
def actualize_points(ctf):
if ctf.category.name == "-Intro-":
return
solves = CTF_flags.objects.filter(ctf=ctf)
nb_solves = len(solves)
new_points = max(200 - int(log(nb_solves)*8.5)*5, 5)
if new_points != ctf.points:
diff = ctf.points - new_points
ctf.points = new_points
ctf.save()
for s in solves:
s.user.userprofileinfo.score -= diff
s.user.userprofileinfo.save()
def category(request, cat_slug):
cat = get_object_or_404(Category, slug=cat_slug)
ctfs = CTF.objects.filter(category=cat).order_by('-points')
ctfs = CTF.objects.filter(category=cat, event=None, disabled=False).order_by('points')
for ex in ctfs:
ex.solved_num = CTF_flags.objects.filter(ctf=ex).count()
ex.solved = ex.solved_by(request.user)
ex.solved_num = CTF_flags.objects.filter(ctf=ex).count()
ex.solved = ex.solved_by(request.user)
return render(request, 'ctfs/ctfs_list.html', {'ctfs' : ctfs, 'cat' : cat})
def ctf(request, cat_slug, ctf_slug):
ctf_info = get_object_or_404(CTF, slug=ctf_slug)
ctf_info = get_object_or_404(CTF, slug=ctf_slug, event=None)
flagged = False
solved_list = CTF_flags.objects.filter(ctf=ctf_info).order_by('flag_date')
description = get_description_by_lang(ctf_info)
if request.user.is_authenticated:
if CTF_flags.objects.filter(user=request.user, ctf=ctf_info):
flagged = True
@ -24,25 +58,20 @@ def ctf(request, cat_slug, ctf_slug):
if request.user.is_authenticated:
form = submit_flag(data=request.POST)
if flagged == False and form.is_valid():
if CTF.objects.filter(flag=request.POST.get('flag'), slug=ctf_slug):
if CTF.objects.filter(flag=request.POST.get('flag'), slug=ctf_slug, event=None):
new = CTF_flags(user = request.user, ctf = ctf_info, flag_date = timezone.now())
new.save()
profil = UserProfileInfo.objects.get(user=request.user)
profil.last_submission_date = timezone.now()
profil.score += ctf_info.points
profil.save()
return render(request, 'ctfs/ctf_info.html', {'form' : form, 'ctf' : ctf_info, 'solved_list': solved_list, 'valitated': True})
actualize_points(ctf_info)
return render(request, 'ctfs/ctf_info.html', { 'ctf' : ctf_info, 'solved_list': solved_list, 'valitated': True, 'description': description, 'date': timezone.now()})
else:
return render(request, 'ctfs/ctf_info.html', {'form' : form, 'ctf' : ctf_info, 'solved_list': solved_list, 'failed': True})
return render(request, 'ctfs/ctf_info.html', { 'ctf' : ctf_info, 'solved_list': solved_list, 'failed': True, 'description': description, 'date': timezone.now()})
else:
form = submit_flag()
return render(request, 'ctfs/ctf_info.html', {'form' : form, 'ctf' : ctf_info, 'solved_list': solved_list, 'alvalitated': True})
return render(request, 'ctfs/ctf_info.html', { 'ctf' : ctf_info, 'solved_list': solved_list, 'alvalitated': True, 'description': description, 'date': timezone.now()})
else:
form = submit_flag()
return render(request, 'ctfs/ctf_info.html', {'form' : form, 'ctf' : ctf_info, 'solved_list': solved_list})
return render(request, 'ctfs/ctf_info.html', { 'ctf' : ctf_info, 'solved_list': solved_list, 'description': description, 'date': timezone.now()})
else:
form = submit_flag()
return render(request, 'ctfs/ctf_info.html', {'form' : form, 'ctf' : ctf_info, 'solved_list': solved_list, 'alvalitated': flagged})
# Create your views here.
return render(request, 'ctfs/ctf_info.html', { 'ctf' : ctf_info, 'solved_list': solved_list, 'alvalitated': flagged, 'description': description, 'date': timezone.now()})

0
src/events/__init__.py Normal file
View File

29
src/events/admin.py Normal file
View File

@ -0,0 +1,29 @@
from django.contrib import admin
from .models import Event, EventPlayer, Team
@admin.register(Event)
class event(admin.ModelAdmin):
#list display
list_display = ['name', 'start_date', 'end_date']
# search list
search_fields = ['name', 'slug', 'description', 'password']
@admin.register(EventPlayer)
class score(admin.ModelAdmin):
#list display
list_display = ['user', 'event', 'score']
#list Filter
list_filter = ('event',)
# search list
search_fields = ['user__username', 'score', 'event__name']
# Register your models here.
@admin.register(Team)
class team(admin.ModelAdmin):
#list display
list_display = ['name', 'event']
#list Filter
list_filter = ('event',)
# search list
search_fields = ['name']

6
src/events/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class EventsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'events'

20
src/events/forms.py Normal file
View File

@ -0,0 +1,20 @@
from django import forms
from .models import Team
class submit_flag(forms.Form):
flag = forms.CharField(label="Flag", max_length=100)
class create_team(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput())
class Meta():
model = Team
fields = ('name','password')
class TeamUpdateForm(forms.ModelForm):
class Meta:
model = Team
fields=('name', 'password',)
def __init__(self, *args, **kwargs):
super(TeamUpdateForm, self).__init__(*args, **kwargs)
for key in self.fields:
self.fields[key].required = True

View File

@ -0,0 +1,46 @@
# Generated by Django 3.2.7 on 2021-10-18 14:19
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
import uuid
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Event',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('name', models.CharField(max_length=200)),
('logo', models.CharField(max_length=200)),
('img', models.CharField(max_length=200)),
('description', models.TextField()),
('start_date', models.DateTimeField()),
('end_date', models.DateTimeField()),
('password', models.CharField(max_length=200)),
('slug', models.SlugField(max_length=55)),
],
),
migrations.CreateModel(
name='Scores',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('score', models.PositiveIntegerField(db_index=True, default=0)),
('last_submission_date', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Last Submission Date')),
('event', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='events.event')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ['-score', 'last_submission_date', 'user__username'],
},
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.7 on 2021-10-18 14:43
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='event',
name='password',
field=models.CharField(blank=True, max_length=200),
),
]

View File

@ -0,0 +1,26 @@
# Generated by Django 3.2.7 on 2021-10-19 15:19
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('events', '0002_alter_event_password'),
]
operations = [
migrations.AlterField(
model_name='scores',
name='event',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='events.event'),
),
migrations.AlterField(
model_name='scores',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
]

View File

@ -0,0 +1,30 @@
# Generated by Django 3.1.5 on 2021-12-27 15:40
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('events', '0003_auto_20211019_1519'),
]
operations = [
migrations.RenameModel('Scores', 'EventPlayer'),
migrations.CreateModel(
name='Team',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
('password', models.CharField(max_length=200)),
('size', models.PositiveIntegerField(default=1)),
('score', models.PositiveIntegerField(db_index=True, default=0)),
('last_submission_date', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Last Submission Date')),
('event', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='events.event')),
],
),
]

View File

@ -0,0 +1,29 @@
# Generated by Django 3.1.5 on 2021-12-27 15:56
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('events', '0004_auto_20211227_1540'),
]
operations = [
migrations.AddField(
model_name='event',
name='team_size',
field=models.PositiveIntegerField(default=1),
),
migrations.AddField(
model_name='eventplayer',
name='team',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='events.team'),
),
migrations.AlterField(
model_name='eventplayer',
name='id',
field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
]

View File

@ -0,0 +1,22 @@
# Generated by Django 3.1.5 on 2022-01-14 23:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0005_auto_20211227_1556'),
]
operations = [
migrations.RemoveField(
model_name='team',
name='size',
),
migrations.AddField(
model_name='team',
name='auto',
field=models.BooleanField(default=False),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.1.5 on 2022-01-16 21:09
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0006_auto_20220114_2319'),
]
operations = [
migrations.AddField(
model_name='event',
name='auto_match',
field=models.BooleanField(default=False),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.1.5 on 2022-02-12 18:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0007_event_auto_match'),
]
operations = [
migrations.AddField(
model_name='event',
name='dynamic',
field=models.BooleanField(default=False),
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 3.2.11 on 2022-02-15 16:06
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('events', '0008_event_dynamic'),
]
operations = [
migrations.AlterField(
model_name='eventplayer',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='team',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
]

View File

42
src/events/models.py Normal file
View File

@ -0,0 +1,42 @@
from django.db import models
from django.contrib.auth.models import User
from django.contrib.auth.models import timezone
import uuid
# Create your models here.
class Event(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=200)
logo = models.CharField(max_length=200)
img = models.CharField(max_length=200)
description = models.TextField()
start_date = models.DateTimeField()
end_date = models.DateTimeField()
password = models.CharField(max_length=200, blank=True)
slug = models.SlugField(max_length=55)
team_size = models.PositiveIntegerField(default=1)
auto_match = models.BooleanField(default=False)
dynamic = models.BooleanField(default=False)
def __str__(self):
return self.name
class Team(models.Model):
name = models.CharField(max_length=200)
password = models.CharField(max_length=200)
event = models.ForeignKey(Event, on_delete=models.CASCADE, null=True)
score = models.PositiveIntegerField(default=0, db_index=True)
last_submission_date = models.DateTimeField('Last Submission Date', default=timezone.now)
auto = models.BooleanField(default=False)
def __str__(self):
return self.name
class EventPlayer(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
event = models.ForeignKey(Event, on_delete=models.CASCADE)
score = models.PositiveIntegerField(default=0, db_index=True)
last_submission_date = models.DateTimeField('Last Submission Date', default=timezone.now)
team = models.ForeignKey(Team, on_delete=models.CASCADE, null=True)
class Meta:
ordering = ['-score', 'last_submission_date', 'user__username']

View File

@ -0,0 +1,64 @@
{% extends 'base.html' %}
{% block content %}
{% load i18n %}
<div class="row">
<div class="col-sm-12 col-md-9">
<div class="ctf-block">
<a href="{% url 'events:event_info' event_slug=event.slug %}">< Back to event</a>
<div class="ctf-head">
<h3>{{ event.name }}</h3>
<small>{% trans "This event starts at" %} : {{ event.start_date }}</small>
</div>
<div class="ctf-footer">
<div class="col-sm-8 col-md-6 mx-auto">
{% if logged == True%}
{% if registered == False %}
<span class="message error-msg">{% trans "You need to be registered to the event." %}</span>
{% else %}
{% if exist == True %}
<span class="message error-msg">{% trans "Name already taken." %}</span>
{% endif %}
<h2>Create Team</h2>
<form method="post" action="{% url 'events:create_team' event_slug=event.slug %}" class="create-team-form">
{% csrf_token %}
<div class="form-group">
<input class="form-control" type="text" name="teamname" placeholder="{% trans "Team name" %} *" maxlength="150" required="" id="id_teamname"></br>
<input class="form-control" type="password" name="password" placeholder="{% trans "Password" %} *" required="" id="id_password"></br>
<input type="submit" name="" class="form-control" value="{% trans "Create Team" %}">
</div>
</form>
{% endif %}
{% else %}
<h4>{% trans "You need to be logged to access this event." %}</h4>
{% endif %}
</div>
</div>
</div>
</div>
<div class="d-none d-md-block col-10 col-md-3 right-sidebar">
<ul class="list-group">
<li class="list-group-item">{{ event.name }}</li>
<li class="list-group-item">{% trans "Starts at" %} : <span style="position:absolute;right: 15px;">{{ event.start_date | date:'H:i d-m-y'}}</span></li>
<li class="list-group-item">{% trans "Ends at" %} : <span style="position:absolute;right: 15px;">{{ event.end_date | date:'H:i d-m-y'}}</span></li>
</ul>
<ul class="list-group">
<li class="list-group-item">{% trans "Manage my team" %}</li>
<a href="{% url 'events:join_team' event_slug=event.slug %}" class="list-group-item">→{% trans "Join Team" %}</a>
<a href="{% url 'events:create_team' event_slug=event.slug %}" class="list-group-item">{% trans "Create Team" %}</a>
</ul>
{% if event.auto_match %}
<ul class="list-group">
<li class="list-group-item">{% trans "Auto-matching" %}</li>
<form method='GET' action="{% url 'events:find_team' event_slug=event.slug %}">
{%csrf_token%}
<li class="list-group-item">
<input class="form-control" type="submit" value="{% trans "Find me a team !" %}">
</li>
</form>
</ul>
{% endif %}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,104 @@
{% extends 'base.html' %}
{% block content %}
{% load i18n %}
{% load is_member %}
<div class="row">
<div class="col-sm-12 col-md-9">
<div class="ctf-block">
<a href="{% url 'events:event_info' event_slug=event.slug %}">< Back to event</a>
<div class="ctf-head">
<h2>{% trans "Event" %} - {{ event.name }}</h2>
<h4>{{ ctf.name }}</h4>
<small>{% trans "Published date" %} : {{ ctf.pub_date }}</small>
</div>
<div class="ctf-body">
{% if description %}
{{ description|safe }}
{% else %}
{% trans "No translation available. Please try another language (English or French)." %}
{% endif %}
</div>
<div class="ctf-footer">
{% if request.user.is_authenticated %}
{% if congrat == True %}
<p>{% trans "Congratulation !" %}</p>
{% elif alreadyflag == True %}
<p>{% trans "Already flagged" %}</p>
{% elif eventisover == True %}
<p>{% trans "This event is over." %}</p>
{% elif errorform == True %}
<p>{% trans "Error while processing your request. (Invalid Form)" %}</p>
{% elif notsub == True %}
<span class="message error-msg">{% trans "You must register to the event before submitting flags." %}</span>
{% elif noteam == True %}
<span class="message error-msg">{% trans "This is a team event, please create or join a team before submitting flags." %}</span>
{% if ctf.ctf_url %}
<a class="begin-ctf-link" target="_blank" href="{{ ctf.ctf_url }}">{% trans "Start the challenge" %}</a></br>
{% elif ctf.file %}
<a class="begin-ctf-link" target="_blank" href="{{ ctf.file.url }}">{% trans "Download" %}</a></br>
{% endif %}
{% else %}
{% if wrongflag == True %}
<p>{% trans "Wrong flag ! You can do it !" %}</p>
{% endif %}
{% if ctf.ctf_url %}
<a class="begin-ctf-link" target="_blank" href="{{ ctf.ctf_url }}">{% trans "Start the challenge" %}</a></br>
{% elif ctf.file %}
<a class="begin-ctf-link" target="_blank" href="{{ ctf.file.url }}">{% trans "Download" %}</a></br>
{% endif %}
<form method="post" action="{% url 'events:submit_event_flag' event_slug=event.slug chall_slug=ctf.slug %}" class="submitflag-form">
{% csrf_token %}
<input type="text" name="flag" maxlength="100" required="" id="id_flag">
<input class="form-control" type="submit" value=">">
</form>
{% endif %}
{% endif %}
</div>
</div>
<div>
<h4>{% trans "Solved by" %}</h4>
{% if solved_list %}
<table class="table table-dark">
<thead>
<tr>
<th scope="col">{% trans "Username" %}</th>
<th scope="col">{% trans "Website" %}</th>
<th scope="col">{% trans "Date" %}</th>
</tr>
</thead>
<tbody>
{% for s in solved_list %}
{% if event.team_size == 1%}
{% ismember s.0.userprofileinfo as is_member %}
<tr>
<th scope="row"><a class="profile_link {{is_member}}" href="{% url 'events:profile' user_name=s.0.username event_slug=event.slug %}"> {{ s.0.username }}</a></th>
<td>{{ s.0.userprofileinfo.portfolio_site }}</td>
<td>{{ s.1 }}</td>
</tr>
{% else %}
<tr>
<th scope="row"><a class="profile_link" href="{% url 'events:team_info' name=s.2 event_slug=event.slug %}"> {{ s.2 }}</a></th>
<td></td>
<td>{{ s.1 }}</td>
</tr>
{% endif %}
{% endfor %}
</tbody>
</table>
{% else %}
<p>{% trans "Nobody has solved this challenge yet." %}</p>
{% endif %}
</div>
</div>
<div class="d-none d-md-block col-10 col-md-3 right-sidebar">
{% ismember ctf.author.userprofileinfo as is_member %}
<ul class="list-group">
<li class="list-group-item">{% trans "Author" %} : <a style="position:absolute;right: 15px;" class="profile_link {{is_member}}" href="{% url 'accounts:profile' user_name=ctf.author.username %}">{{ ctf.author.username }}</a></li>
<li class="list-group-item">{% trans "Point reward" %} : <span style="position:absolute;right: 15px;">{{ ctf.points }}</span></li>
</ul>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,137 @@
{% extends 'base.html' %}
{% block content %}
{% load i18n %}
{% load is_flagged %}
{% load is_member %}
<div class="row">
<div class="col-sm-12 col-md-9">
{% if subisover == True %}
<span class="message error-msg">{% trans "Subscriptions is over." %}</span>
{% endif %}
{% if alreadyregistered == True %}
<span class="message error-msg">{% trans "You're already registered to this event." %}</span>
{% endif %}
<div class="event-block">
<div class="event-head" style="background-image:linear-gradient(180deg, rgba(102,102,102,0.3) 100%, rgba(29,29,29,1) 100%),url('{{ event.img }}');">
<h3>{{ event.name }}</h3>
{% if ended == True %}
<small>{% trans "This event is over." %}</small>
{% else %}
<small>{% trans "This event start at" %} : {{ event.start_date }}</small>
{% endif %}
</div>
<div class="event-body">
{% if event.description %}
{{ event.description|safe }}
{% endif %}
</div>
<div class="event-footer">
{% if begun == True %}
<h4>{% trans "Challenges" %}</h4>
{% if ctfs %}
<div class="row">
{% for ctf in ctfs %}
<div class="col-md-4">
{% isflagged request.user ctf as flagged%}
<a href="{% url 'events:event_chall_info' event_slug=event.slug chall_slug=ctf.slug %}">
<div class="chall-card {{flagged}}">
<p>{{ ctf.category }}</p>
<p>{{ ctf.name }} - {{ ctf.points }}</p>
</div>
</a>
</div>
{% endfor %}
</div>
{% else %}
<small>{% trans "No challenges available." %}</small>
{% endif %}
{% else %}
<h4>{% trans "The event has not started yet." %}</h4>
{% endif %}
</div>
</div>
<div>
<h4>{% trans "ScoreBoard" %}</h4>
{% if solved_list %}
{% if event.team_size == 1 %}
<table class="table table-dark">
<thead>
<tr>
<th scope="col">{% trans "Rank" %}</th>
<th scope="col">{% trans "Username" %}</th>
<th scope="col">{% trans "Website" %}</th>
<th scope="col">{% trans "Score" %}</th>
</tr>
</thead>
<tbody>
{% for s in solved_list %}
<tr>
{% ismember s.user.userprofileinfo as is_member %}
<th scope="row"># {{ forloop.counter0|add:1 }}</th>
<th scope="row">
<a class="profile_link {{is_member}}" href="{% url 'events:profile' user_name=s.user.username event_slug=event.slug %}"> {{ s.user.username }}</a>
</th>
<td>{{ s.user.userprofileinfo.site }}</td>
<td>{{ s.score }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<table class="table table-dark">
<thead>
<tr>
<th scope="col">{% trans "Rank" %}</th>
<th scope="col">{% trans "Team" %}</th>
<th scope="col">{% trans "Score" %}</th>
</tr>
</thead>
<tbody>
{% for s in solved_list %}
<tr>
<th scope="row"># {{ forloop.counter0|add:1 }}</th>
<th scope="row">
<a class="profile_link" href="{% url 'events:team_info' name=s.name event_slug=event.slug %}"> {{ s.name }}</a>
</th>
<td>{{ s.score }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% else %}
<p>{% trans "No one have earn point yet, you gonna be the first ?" %}</p>
{% endif %}
</div>
</div>
<div class="d-none d-md-block col-10 col-md-3 right-sidebar">
<ul class="list-group">
<li class="list-group-item">{{ event.name }}</li>
<li class="list-group-item">{% trans "Starts at" %} : <span style="position:absolute;right: 15px;">{{ event.start_date | date:'H:i d-m-y'}}</span></li>
<li class="list-group-item">{% trans "Ends at" %} : <span style="position:absolute;right: 15px;">{{ event.end_date | date:'H:i d-m-y'}}</span></li>
{% if ended == False and IsRegistered == False %}
<form method='POST' action="{% url 'events:register_event' event_slug=event.slug %}">
{%csrf_token%}
<li class="list-group-item">
<input class="form-control" type="submit" value="{% trans "Register" %}">
</li>
</form>
{% endif %}
</ul>
{% if event.team_size > 1 and IsRegistered == True and ended == False %}
<ul class="list-group">
<form method='GET' action="{% url 'events:manage_team' event_slug=event.slug %}">
{%csrf_token%}
<li class="list-group-item">
<input class="form-control" type="submit" value="{% trans "Manage my team" %}">
</li>
</form>
</ul>
{% endif %}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,43 @@
{% extends 'base.html' %}
{% block content %}
{% load i18n %}
<div class="row">
<div class="col-sm-12 col-md-9">
<div class="ctf-block">
<div class="ctf-head">
<h3>{{ event.name }}</h3>
<small>{% trans "This event start at" %} : {{ event.start_date }}</small>
</div>
<div class="ctf-footer">
{% if logged == True %}
{% if wrongpwd == True %}
<span class="message error-msg">{% trans "Wrong password submited." %}</span>
{% endif %}
{% if alreadyregistered == True %}
<span class="message error-msg">{% trans "You're already registered to this event." %}</span>
{% endif %}
<h4>{% trans "This event is password protected" %}</h4>
<small>{% trans "You need to submit the event password to gain access to this event." %}</small>
<form method="post" action="{% url 'events:submit_pwd' event_slug=event.slug %}" class="submitflag-form">
{% csrf_token %}
<input type="text" name="password" maxlength="48" required="">
<input class="form-control" type="submit" value=">">
</form>
{% else %}
<h4>{% trans "You need to be logged to access this event." %}</h4>
{% endif %}
</div>
</div>
</div>
<div class="d-none d-md-block col-10 col-md-3 right-sidebar">
<ul class="list-group">
<li class="list-group-item">{{ event.name }}</li>
<li class="list-group-item">{% trans "Starts at" %} : {{ event.start_date | date:'H:i d-m-y'}}</li>
<li class="list-group-item">{% trans "Ends at" %} : {{ event.end_date | date:'H:i d-m-y'}}</li>
</ul>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,49 @@
{% extends 'base.html' %}
{% block content %}
{% load i18n %}
<div class="row">
<div class="col-12">
<h3>{% trans "Events" %}</h3>
</div>
{% if events %}
{% for ev in events %}
{% if curdate > ev.end_date %}
<div class="col-md-3 is-over">
{% else %}
<div class="col-md-3">
{% endif %}
<div class="event-card">
<img
src="{{ev.logo}}"
class="card-img-top"
alt="{{ ev.name }}"
/>
<div class="card-body">
<h5 class="card-title">
{{ ev.name }}
</h5>
</div>
<ul class="list-group list-group-flush text-center">
<li class="list-group-item">
{{ ev.start_date }} <br> - <br> {{ ev.end_date }}
<br>
{% if curdate > ev.end_date %}
<span class="badge badge-pill badge-secondary">Finished</span>
{% else %}
<span class="badge badge-pill badge-success">Open</span>
{% endif %}
</li>
</ul>
<div class="card-body text-center">
<a href="{% url 'events:event_info' event_slug=ev.slug %}" class="card-link">{% trans "See more" %}</a>
</div>
</div>
</div>
{% endfor %}
{% else %}
<p>{% trans "No events available." %}</p>
{% endif %}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,69 @@
{% extends 'base.html' %}
{% block content %}
{% load i18n %}
<div class="row">
<div class="col-sm-12 col-md-9">
<div class="ctf-block">
<a href="{% url 'events:event_info' event_slug=event.slug %}">< Back to event</a>
<div class="ctf-head">
<h3>{{ event.name }}</h3>
<small>{% trans "This event starts at" %} : {{ event.start_date }}</small>
</div>
<div class="ctf-footer">
<div class="col-sm-8 col-md-6 mx-auto">
{% if logged == True%}
{% if registered == False %}
<span class="message error-msg">{% trans "You need to be registered to the event." %}</span>
{% else %}
{% if notexist == True %}
<span class="message error-msg">{% trans "Team does not exist." %}</span>
{% elif wrongpwd == True %}
<span class="message error-msg">{% trans "Wrong password submited." %}</span>
{% elif max == True %}
<span class="message error-msg">{% trans "Maximum size reached." %}</span>
{% elif exist == True %}
{% endif %}
<h2>Join Team</h2>
<form method="post" action="{% url 'events:join_team' event_slug=event.slug %}" class="join-team-form">
{% csrf_token %}
<div class="form-group">
<input class="form-control" type="text" name="teamname" placeholder="{% trans "Team name" %} *" maxlength="150" required="" id="id_teamname"></br>
<input class="form-control" type="password" name="password" placeholder="{% trans "Password" %} *" required="" id="id_password"></br>
<input type="submit" name="" class="form-control" value="{% trans "Join Team" %}">
</div>
</form>
{% endif %}
{% else %}
<h4>{% trans "You need to be logged to access this event." %}</h4>
{% endif %}
</div>
</div>
</div>
</div>
<div class="d-none d-md-block col-10 col-md-3 right-sidebar">
<ul class="list-group">
<li class="list-group-item">{{ event.name }}</li>
<li class="list-group-item">{% trans "Starts at" %} : <span style="position:absolute;right: 15px;">{{ event.start_date | date:'H:i d-m-y'}}</span></li>
<li class="list-group-item">{% trans "Ends at" %} : <span style="position:absolute;right: 15px;">{{ event.end_date | date:'H:i d-m-y'}}</span></li>
</ul>
<ul class="list-group">
<li class="list-group-item">{% trans "Manage my team" %}</li>
<a href="{% url 'events:join_team' event_slug=event.slug %}" class="list-group-item">{% trans "Join Team" %}</a>
<a href="{% url 'events:create_team' event_slug=event.slug %}" class="list-group-item">{% trans "Create Team" %}</a>
</ul>
{% if event.auto_match %}
<ul class="list-group">
<li class="list-group-item">{% trans "Auto-matching" %}</li>
<form method='GET' action="{% url 'events:find_team' event_slug=event.slug %}">
{%csrf_token%}
<li class="list-group-item">
<input class="form-control" type="submit" value="{% trans "Find me a team !" %}">
</li>
</form>
</ul>
{% endif %}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,73 @@
{% extends 'base.html' %}
{% block content %}
{% load i18n %}
<div class="row">
<div class="col-sm-12 col-md-9">
<div class="ctf-block">
<a href="{% url 'events:event_info' event_slug=player.event.slug %}">< Back to event</a>
<div class="ctf-head">
<h3>Edit info</h3>
</div>
<div class="bloc-body">
<div class="col-sm-12 col-md-12 mx-auto">
<!-- {{ u_form.non_field_errors }} -->
{% if error is not None %}
<span class="message error-msg">{{ error }}</span>
{% elif success is not None %}
<span class="message success-msg">{{ success }}</span>
{% endif %}
<form method='POST'>
<div class="edit-infos-grp">
{%csrf_token%}
<label for="{{ p_form.name.id_for_label }}">{% trans "Team name" %} *</label>
{{ p_form.name.errors}}
{{p_form.name}}
</br>
<label for="{{ p_form.password.id_for_label }}">{% trans "Team password" %} *</label>
{{p_form.password}}
</br>
<input class="form-control" type="submit" value="{% trans "Apply" %}">
</div>
</form>
</div>
</div>
</div>
</div>
<div class="d-none d-md-block col-10 col-md-3 right-sidebar">
<ul class="list-group">
<li class="list-group-item">{{ player.team.name }}</li>
<li class="list-group-item">{% trans "Score" %} : {{ player.team.score }}</li>
<!-- <li class="list-group-item">{% trans "Rank" %} : {{ rank }}</li> -->
</ul>
<ul class="list-group">
<li class="list-group-item">{% trans "Members" %}</li>
{% for p in members %}
<li class="list-group-item"><a class="profile_link" href="{% url 'accounts:profile' user_name=p.user.username %}">{{ p.user.username }}</a></li>
{% endfor %}
</ul>
<form method='POST' action="{% url 'events:leave_team' event_slug=player.event.slug %}">
{%csrf_token%}
<li class="list-group-item">
<input class="form-control" type="submit" value="{% trans "Leave Team" %}">
</li>
</form>
{% if player.team.auto == False and player.event.auto_match == True %}
<form method='POST' action="{% url 'events:open_team' event_slug=player.event.slug %}">
{%csrf_token%}
<li class="list-group-item">
<input class="form-control" type="submit" value="{% trans "Open to automatching" %}">
</li>
</form>
{% elif player.event.auto_match == True %}
<form method='POST' action="{% url 'events:close_team' event_slug=player.event.slug %}">
{%csrf_token%}
<li class="list-group-item">
<input class="form-control" type="submit" value="{% trans "Close to automatching" %}">
</li>
</form>
{% endif %}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,145 @@
{% extends 'base.html' %}
{% block content %}
{% load i18n %}
{% load key_value %}
<div class="row">
<div class="col-sm-12 col-md-9">
<a href="{% url 'events:event_info' event_slug=event.slug %}">< Back to event</a>
<div>
<h4>Challenges Solved by {{ team.name }} - {{ event.name }}</h4>
{% if solves%}
<div class="table table-dark">
<div class="card-body">
<div id="time-chart"></div>
</div>
</div>
<table class="table table-dark">
<thead>
<tr>
<th scope="col">{% trans "Challenge Name" %}</th>
<th scope="col">{% trans "Category" %}</th>
<th scope="col">{% trans "Points" %}</th>
<th scope="col">{% trans "Date" %}</th>
</tr>
</thead>
<tbody>
{% for s in solves %}
<tr>
<th scope="row"><a href="{% url 'events:event_chall_info' event_slug=event.slug chall_slug=s.ctf.slug %}">{{ s.ctf.name }}</a></th>
<td>{{ s.ctf.category.name}}</td>
<td>{{ s.ctf.points }}</td>
<td>{{ s.flag_date|date:"Y-m-d H:i:s" }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p>{% trans "It seems that this team has not solved any challenge yet..." %}</p>
{% endif %}
</div>
</div>
<div class="d-none d-md-block col-10 col-md-3 right-sidebar">
<ul class="list-group">
<li class="list-group-item">{{ team.name }}</li>
<li class="list-group-item">{% trans "Score" %} : {{ score }}</li>
<li class="list-group-item">{% trans "Rank" %} : {{ rank }}</li>
</ul>
<ul class="list-group">
<li class="list-group-item">{% trans "Members" %}</li>
{% for user in users %}
<li class="list-group-item"><a class="profile_link" href="{% url 'accounts:profile' user_name=user.username %}">{{ user.username }}</a></li>
{% endfor %}
</ul>
<ul class="list-group">
<li class="list-group-item">{% trans "Categories stats" %}</li>
{% for cat in catsDatas %}
<li class="list-group-item" style="padding-bottom: 3;padding-top: 0;">
<span>{{ cat.0 }}</span>
<div class="progress">
{% if cat.3 == '0' %}
<div class="progress-bar bg-success" role="progressbar" style="width: 0%;color:#d9d9d9;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">0 %</div>
{% else %}
<div class="progress-bar bg-success" role="progressbar" style="width: {{ cat.3 }}%" aria-valuenow="{{ cat.3 }}" aria-valuemin="0" aria-valuemax="100">{{ cat.3 }} %</div>
{% endif %}
</div>
</li>
{% endfor %}
</ul>
</div>
</div>
<script src="https://code.highcharts.com/highcharts.src.js"></script>
<script>
Highcharts.theme={colors:["#2b908f","#90ee7e","#f45b5b","#7798BF","#aaeeee","#ff0066","#eeaaee","#55BF3B","#DF5353","#7798BF","#aaeeee"],chart:{backgroundColor:{linearGradient:{x1:0,y1:0,x2:1,y2:1},stops:[[0,"#1D1D1D"],[1,"#1D1D1D"]]},style:{fontFamily:"'Unica One', sans-serif"},plotBorderColor:"#606063"},title:{style:{color:"#E0E0E3",textTransform:"uppercase",fontSize:"20px"}},subtitle:{style:{color:"#E0E0E3",textTransform:"uppercase"}},xAxis:{gridLineColor:"#707073",labels:{style:{color:"#E0E0E3"}},lineColor:"#707073",minorGridLineColor:"#505053",tickColor:"#707073",title:{style:{color:"#A0A0A3"}}},yAxis:{gridLineColor:"#707073",labels:{style:{color:"#E0E0E3"}},lineColor:"#707073",minorGridLineColor:"#505053",tickColor:"#707073",tickWidth:1,title:{style:{color:"#A0A0A3"}}},tooltip:{backgroundColor:"rgba(0, 0, 0, 0.85)",style:{color:"#F0F0F0"}},plotOptions:{series:{dataLabels:{color:"#F0F0F3",style:{fontSize:"13px"}},marker:{lineColor:"#333"}},boxplot:{fillColor:"#505053"},candlestick:{lineColor:"white"},errorbar:{color:"white"}},legend:{backgroundColor:"#1D1D1D",itemStyle:{color:"#E0E0E3"},itemHoverStyle:{color:"#FFF"},itemHiddenStyle:{color:"#606063"},title:{style:{color:"#C0C0C0"}}},credits:{style:{color:"#666"}},labels:{style:{color:"#707073"}},drilldown:{activeAxisLabelStyle:{color:"#F0F0F3"},activeDataLabelStyle:{color:"#F0F0F3"}},navigation:{buttonOptions:{symbolStroke:"#DDDDDD",theme:{fill:"#505053"}}},rangeSelector:{buttonTheme:{fill:"#505053",stroke:"#000000",style:{color:"#CCC"},states:{hover:{fill:"#707073",stroke:"#000000",style:{color:"white"}},select:{fill:"#000003",stroke:"#000000",style:{color:"white"}}}},inputBoxBorderColor:"#505053",inputStyle:{backgroundColor:"#333",color:"silver"},labelStyle:{color:"silver"}},navigator:{handles:{backgroundColor:"#666",borderColor:"#AAA"},outlineColor:"#CCC",maskFill:"rgba(255,255,255,0.1)",series:{color:"#7798BF",lineColor:"#A6C7ED"},xAxis:{gridLineColor:"#505053"}},scrollbar:{barBackgroundColor:"#808083",barBorderColor:"#808083",buttonArrowColor:"#CCC",buttonBackgroundColor:"#606063",buttonBorderColor:"#606063",rifleColor:"#FFF",trackBackgroundColor:"#404043",trackBorderColor:"#404043"}};
Highcharts.setOptions(Highcharts.theme);
Highcharts.chart('time-chart', {
title: {
text: 'Points earned for each category'
},
yAxis: {
title: {
text: 'Points earned'
}
},
xAxis: {
type: 'datetime',
labels: {
formatter: function() {
return Highcharts.dateFormat('%d.%b %Y',
this.value);
}
}
},
legend: {
layout: 'vertical',
align: 'right',
verticalAlign: 'middle'
},
plotOptions: {
pointStart: {{ event.start_date|timestamp_fromdate }},
series: {
label: {
connectorAllowed: false
},
allowPointSelect: true,
marker: {
enabled: true
}
}
},
series: [
{
name: 'Total',
data: {{ solved|safe }}
},
{% for cat in cats %}
{
name: '{{ cat.name }}',
data: {{ pointDatas|keyvalue:cat.name|safe }},
visible: false,
},
{% endfor %}
],
responsive: {
rules: [{
condition: {
maxWidth: 500
},
chartOptions: {
legend: {
layout: 'horizontal',
align: 'center',
verticalAlign: 'bottom'
}
}
}]
}
});
</script>
{% endblock %}

View File

View File

@ -0,0 +1,27 @@
from django import template
from ctfs.models import CTF_flags
from events.models import EventPlayer
register = template.Library()
@register.simple_tag
def isflagged(user, ctf):
flagged = False
event_info = ctf.event
if user.is_authenticated == False:
return ""
if event_info.team_size == 1:
if CTF_flags.objects.filter(user=user, ctf=ctf):
flagged = True
elif EventPlayer.objects.filter(user=user, event=event_info):
player = EventPlayer.objects.get(user=user, event=event_info)
solved_list = CTF_flags.objects.filter(ctf=ctf)
for s in solved_list:
tmp = EventPlayer.objects.get(user=s.user, event=event_info)
if tmp.team == player.team:
flagged = True
if flagged:
return "success-msg"
return ""

3
src/events/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

22
src/events/urls.py Normal file
View File

@ -0,0 +1,22 @@
from django.urls import path
from . import views
app_name = "events"
urlpatterns = [
path('', views.events, name='events'),
path('<str:event_slug>', views.event, name='event_info'),
path('<str:event_slug>/challenge/<str:chall_slug>', views.chall_event_info, name='event_chall_info'),
path('pwd/<str:event_slug>', views.submit_pwd, name='submit_pwd'),
path('submitEventFlag/<str:event_slug>/<str:chall_slug>', views.submit_event_flag, name='submit_event_flag'),
path('register/<str:event_slug>', views.register_to_event, name='register_event'),
path('create_team/<str:event_slug>', views.create_team, name='create_team'),
path('join_team/<str:event_slug>', views.join_team, name='join_team'),
path('<str:event_slug>/profile/<str:user_name>', views.profile, name='profile'),
path('<str:event_slug>/team/<str:name>', views.team_info, name='team_info'),
path('<str:event_slug>/manage_team', views.manage_team, name='manage_team'),
path('<str:event_slug>/leave_team', views.leave_team, name='leave_team'),
path('find_team/<str:event_slug>', views.find_team, name='find_team'),
path('<str:event_slug>/open_team', views.open_team, name='open_team'),
path('<str:event_slug>/close_team', views.close_team, name='close_team'),
]

10
src/events/utils.py Normal file
View File

@ -0,0 +1,10 @@
from random import choice, randint
colors = ['blue', 'red', 'yellow', 'green', 'black', 'white', 'purple', 'orange', 'brown', 'fuchsia', 'gold', 'pink', 'cyan',
'magenta', 'pearl']
animals = ['tiger', 'bee', 'dog', 'cat', 'otter', 'lizard', 'horse', 'mouse', 'butterfly', 'dolphin', 'elephant', 'falcon', 'goat',
'cow', 'lion', 'ostrich']
def get_random_name():
return choice(colors) + choice(animals) + str(randint(0, 100))

View File

@ -0,0 +1,2 @@
from .events import *
from .teams import *

283
src/events/views/events.py Normal file
View File

@ -0,0 +1,283 @@
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import timezone
from ..forms import submit_flag
from ..models import Event, EventPlayer, Team
from ctfs.models import CTF, CTF_flags, Category
from django.utils.translation import get_language
from django.contrib.auth.models import User
from django.utils.translation import gettext_lazy as _
from math import log
def get_description_by_lang(ctf):
lang = get_language()
ret = None
if lang == "fr":
ret = ctf.description
elif lang == "en":
ret = ctf.description_en
elif lang == "de":
ret = ctf.description_de
elif lang == "ru":
ret = ctf.description_ru
return ret
def actualize_points(ctf):
solves = CTF_flags.objects.filter(ctf=ctf)
nb_solves = len(solves)
new_points = max(50 - int(log(nb_solves)*2.5)*5, 5)
if new_points != ctf.points:
diff = ctf.points - new_points
ctf.points = new_points
ctf.save()
for s in solves:
player = EventPlayer.objects.get(event=ctf.event, user=s.user)
player.score -= diff
player.save()
if player.team:
player.team.score -= diff
player.team.save()
# Create your views here.
def events(request):
list_events = Event.objects.filter().order_by('-end_date', 'start_date')
return render(request, 'events/events_list.html', {'events' : list_events, 'curdate': timezone.now()})
def chall_event_info(request, event_slug, chall_slug):
event_info = get_object_or_404(Event, slug=event_slug)
ctf_info = get_object_or_404(CTF, event__slug=event_info.slug, slug=chall_slug)
if timezone.now() < ctf_info.pub_date:
return redirect('events:event_info', event_slug=event_slug)
eventisover = False
alreadyflag = False
congrat = False
wrongflag = False
errorform = False
notsub = False
noteam = False
player = None
if request.user.is_authenticated and not request.user.is_staff:
player = EventPlayer.objects.filter(event=event_info, user=request.user)
if not player:
return redirect('events:event_info', event_slug=event_slug)
elif not request.user.is_authenticated:
return redirect('accounts:signin')
if request.GET.get('EventIsOver') or timezone.now() > event_info.end_date:
eventisover = True
if request.GET.get('AlreadyFlagged'):
alreadyflag = True
if request.GET.get('Congrat'):
congrat = True
if request.GET.get('WrongFlag'):
wrongflag = True
if request.GET.get('ErrorInForm'):
errorform = True
if request.GET.get('NotRegistered'):
notsub = True
if request.GET.get('NoTeam'):
noteam = True
solved_challs = CTF_flags.objects.filter(ctf=ctf_info).order_by('flag_date')
solved_list = []
for s in solved_challs:
if event_info.team_size > 1:
solved_list.append([s.user, s.flag_date, EventPlayer.objects.get(event=event_info, user=s.user).team.name])
else:
solved_list.append([s.user, s.flag_date])
description = get_description_by_lang(ctf_info)
return render(request, 'events/ctf_info.html', { 'ctf' : ctf_info, 'event':event_info, 'solved_list': solved_list, 'description': description, 'eventisover': eventisover, 'alreadyflag': alreadyflag,
'congrat': congrat, 'wrongflag': wrongflag, 'errorform': errorform, 'notsub': notsub, 'noteam':noteam})
def event(request, event_slug):
event_info = get_object_or_404(Event, slug=event_slug)
IsRegistered = False
wrongpwd = False
alreadyregistered = False
subisover = False
if request.GET.get('WrongPassword'):
wrongpwd = True
if request.GET.get('AlreadyRegistered'):
alreadyregistered = True
if request.GET.get('SubscriptionIsOver'):
subisover = True
if request.user.is_authenticated:
try:
player = EventPlayer.objects.get(event=event_info, user=request.user)
except:
player = None
if player:
IsRegistered = True
if event_info.password:
if request.user.is_authenticated:
if request.user.is_staff is False:
if not player:
return render(request, 'events/event_pwd.html', {'event' : event_info, 'logged': True, 'wrongpwd': wrongpwd, 'alreadyregistered': alreadyregistered})
else:
return render(request, 'events/event_pwd.html', {'event' : event_info, 'logged': False, 'wrongpwd': wrongpwd, 'alreadyregistered': alreadyregistered})
ended = False
if timezone.now() >= event_info.end_date:
ended = True
begun = False
if timezone.now() >= event_info.start_date:
begun = True
challenges = CTF.objects.filter(event=event_info, pub_date__lte=timezone.now()).order_by('category', 'points')
if event_info.team_size == 1:
solved_list = EventPlayer.objects.filter(event=event_info).order_by('-score', 'last_submission_date', 'user__username')
else:
solved_list = Team.objects.filter(event=event_info).order_by('-score', 'last_submission_date', 'name')
return render(request, 'events/event_info.html', {'event' : event_info, 'IsRegistered': IsRegistered, 'ctfs': challenges, 'solved_list':solved_list,
'ended': ended, 'begun': begun, 'wrongpwd': wrongpwd, 'alreadyregistered': alreadyregistered, 'subisover': subisover})
@login_required
def submit_event_flag(request, event_slug, chall_slug):
ev = get_object_or_404(Event, slug=event_slug)
response = redirect('events:event_chall_info', event_slug=event_slug, chall_slug=chall_slug)
flagged = False
if timezone.now() >= ev.end_date:
response['Location'] += '?EventIsOver=1'
return response
if request.method == 'POST':
ctf_info = CTF.objects.get(event=ev, slug=chall_slug)
if not ctf_info:
response['Location'] += '?ChallengeNotFound=1'
return response
try:
player = EventPlayer.objects.get(event=ev, user=request.user)
except:
player = None
if player:
if ev.team_size > 1 and player.team is None:
response['Location'] += '?NoTeam=1'
return response
if ev.team_size == 1 and CTF_flags.objects.filter(user=request.user, ctf=ctf_info):
flagged = True
else:
solved_list = CTF_flags.objects.filter(ctf=ctf_info)
for s in solved_list:
tmp = EventPlayer.objects.get(user=s.user, event=ev)
if tmp.team == player.team:
flagged = True
if flagged == True:
response['Location'] += '?AlreadyFlagged=1'
return response
form = submit_flag(data=request.POST)
if form.is_valid():
if ctf_info.flag == request.POST.get('flag'):
new = CTF_flags(user = request.user, ctf = ctf_info, flag_date = timezone.now())
new.save()
if ctf_info.points > 0:
player.last_submission_date = timezone.now()
player.score += ctf_info.points
player.save()
if player.team:
if ctf_info.points > 0:
player.team.last_submission_date = timezone.now()
player.team.score += ctf_info.points
player.team.save()
if ev.dynamic:
actualize_points(ctf_info)
response['Location'] += '?Congrat=1'
return response
else:
response['Location'] += '?WrongFlag=1'
return response
else:
response['Location'] += '?ErrorInForm=1'
return response
else:
response['Location'] += '?NotRegistered=1'
return response
return response
@login_required
def submit_pwd(request, event_slug):
response = redirect('events:event_info', event_slug=event_slug)
event_info = get_object_or_404(Event, slug=event_slug)
if request.method == 'POST':
if request.user.is_authenticated:
ev = get_object_or_404(Event, slug=event_slug)
if ev == False:
response['Location'] += '?NoEventFound=1'
return response
if request.POST.get('password') != ev.password:
response['Location'] += '?WrongPassword=1'
return response
if EventPlayer.objects.filter(user=request.user, event=ev).exists() or EventPlayer.objects.filter(user=request.user, event=ev).exists():
response['Location'] += '?AlreadyRegistered=1'
return response
else:
new = EventPlayer(user=request.user, event=ev)
new.save()
return redirect('events:event_info', event_slug=event_slug)
@login_required
def register_to_event(request, event_slug):
response = redirect('events:event_info', event_slug=event_slug)
if request.method == 'POST':
if request.user.is_authenticated:
ev = get_object_or_404(Event, slug=event_slug)
if ev == False:
response['Location'] += '?NoEventFound=1'
return response
if timezone.now() >= ev.end_date:
response['Location'] += '?SubscriptionIsOver=1'
return response
if EventPlayer.objects.filter(user=request.user, event=ev).exists():
response['Location'] += '?AlreadyRegistered=1'
return response
else:
new = EventPlayer(user=request.user, event=ev, score=0)
new.save()
return redirect('events:event_info', event_slug=event_slug)
@login_required
def profile(request, user_name, event_slug):
catsDatas = []
event_info = get_object_or_404(Event, slug=event_slug)
user_obj = get_object_or_404(User, username=user_name)
player = EventPlayer.objects.get(user=user_obj, event=event_info)
all_players = list(EventPlayer.objects.filter(event=event_info).order_by('-score', 'last_submission_date', 'user__username'))
rank = all_players.index(get_object_or_404(EventPlayer, user=user_obj, event=event_info)) + 1
all_cats = Category.objects.all()
cats = [cat for cat in all_cats if CTF.objects.filter(category__name=cat.name, event=event_info)]
pointDatas = {}
for cat in cats:
# prepare categories
solved_count = CTF_flags.objects.filter(user=user_obj, ctf__event=event_info , ctf__category__name=cat.name).count()
max_count = CTF.objects.filter(category__name=cat.name, event=event_info).count()
# get datas
somme = 0
solved = CTF_flags.objects.filter(user=user_obj, ctf__category__name=cat.name, ctf__event=event_info).order_by('flag_date')
pointDatas[cat.name] = []
pointDatas[cat.name].append([event_info.start_date.timestamp() * 1000, 0])
percent = (solved_count / max_count) * 100
catsDatas.append([cat.name, solved_count, max_count, '{:.0f}'.format(percent)])
for flag in solved:
somme += flag.ctf.points
pointDatas[cat.name].append([flag.flag_date.timestamp() * 1000, somme])
solves = CTF_flags.objects.filter(user=user_obj, ctf__event=event_info).order_by('-flag_date')
solved = []
somme = 0
solved.append([event_info.start_date.timestamp() * 1000, 0])
for s in solves.reverse():
somme += s.ctf.points
solved.append([s.flag_date.timestamp() * 1000,somme])
return render(request,'accounts/profile.html', {'user':user_obj, 'solves':solves,'solved':solved,'catsDatas': catsDatas, 'pointDatas': pointDatas,
'rank': rank, 'score' : somme, 'cats':cats})

202
src/events/views/teams.py Normal file
View File

@ -0,0 +1,202 @@
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required
from ..forms import TeamUpdateForm
from ..models import Event, EventPlayer, Team
from ctfs.models import CTF, CTF_flags, Category
from django.contrib.auth.models import User
from django.db.models import Q
from django.utils.translation import gettext_lazy as _
from ..utils import get_random_name
from random import randint
@login_required
def create_team(request, event_slug):
ev = get_object_or_404(Event, slug=event_slug)
if request.method == 'POST':
if request.user.is_authenticated and ev.team_size > 1:
if Team.objects.filter(name=request.POST.get('teamname'), event=ev).exists():
return render(request, 'events/create_team.html', {'event' : ev, 'logged': True, 'wrongpwd': False, 'registered' : True, 'exist' : True})
new = Team(name=request.POST.get('teamname'), password=request.POST.get('password'), event=ev)
new.save()
player = EventPlayer.objects.get(user=request.user, event=ev)
player.team = new
player.save()
return redirect('events:event_info', event_slug=event_slug)
else:
return render(request, 'events/create_team.html', {'event' : ev, 'logged': True, 'wrongpwd': False, 'registered' : True, 'exist' : False})
@login_required
def join_team(request, event_slug):
ev = get_object_or_404(Event, slug=event_slug)
if request.method == 'POST':
if request.user.is_authenticated and ev.team_size > 1:
try:
team = Team.objects.get(name=request.POST.get('teamname'), event=ev)
except:
team = None
if team is None:
return render(request, 'events/join_team.html', {'event' : ev, 'logged': True, 'wrongpwd': True, 'registered' : True, 'notexist' : True})
else:
members = EventPlayer.objects.filter(team=team)
if request.POST.get('password') != team.password:
return render(request, 'events/join_team.html', {'event' : ev, 'logged': True, 'wrongpwd': True, 'registered' : True, 'notexist' : False})
if members.count() >= ev.team_size:
return render(request, 'events/join_team.html', {'event' : ev, 'logged': True, 'wrongpwd': False, 'registered' : True, 'notexist' : False, 'max' : True})
else:
player = EventPlayer.objects.get(user=request.user, event=ev)
player.team = team
player.save()
return redirect('events:event_info', event_slug=event_slug)
else:
return render(request, 'events/join_team.html', {'event' : ev, 'logged': True, 'wrongpwd': False, 'registered' : True, 'notexist' : False})
@login_required
def team_info(request, name, event_slug):
event_info = get_object_or_404(Event, slug=event_slug)
team = Team.objects.get(name=name, event=event_info)
catsDatas = []
players = EventPlayer.objects.filter(team=team, event=event_info)
users = [p.user for p in players]
all_teams = list(Team.objects.filter(event=event_info).order_by('-score', 'last_submission_date', 'name'))
rank = all_teams.index(get_object_or_404(Team, id=team.id, event=event_info)) + 1
all_cats = Category.objects.all()
cats = [cat for cat in all_cats if CTF.objects.filter(category__name=cat.name, event=event_info)]
pointDatas = {}
for cat in cats:
# prepare categories
solved_count = 0
solved = []
max_count = CTF.objects.filter(category__name=cat.name, event=event_info).count()
somme = 0
pointDatas[cat.name] = [[event_info.start_date.timestamp()*1000, 0]]
for user_obj in users:
# get datas
solved_count += CTF_flags.objects.filter(user=user_obj, ctf__event=event_info , ctf__category__name=cat.name).count()
solved += CTF_flags.objects.filter(user=user_obj, ctf__category__name=cat.name, ctf__event=event_info).order_by('flag_date')
percent = (solved_count / max_count) * 100
catsDatas.append([cat.name, solved_count, max_count, '{:.0f}'.format(percent)])
for flag in solved:
somme += flag.ctf.points
pointDatas[cat.name].append([flag.flag_date.timestamp() * 1000, somme])
query = Q()
for user_obj in users:
query |= Q(user=user_obj)
query &= Q(ctf__event=event_info)
solves = CTF_flags.objects.filter(query).order_by('-flag_date')
solved = []
somme = 0
solved.append([event_info.start_date.timestamp() * 1000, 0])
for s in solves.reverse():
somme += s.ctf.points
solved.append([s.flag_date.timestamp() * 1000,somme])
return render(request,'events/team.html', {'users':users, 'solves':solves,'solved':solved,'catsDatas': catsDatas, 'pointDatas': pointDatas,
'rank': rank, 'team':team, 'score':somme, 'event':event_info, 'cats':cats})
@login_required
def manage_team(request, event_slug):
event_info = get_object_or_404(Event, slug=event_slug)
player = EventPlayer.objects.get(user=request.user, event=event_info)
if not player.team:
return render(request, 'events/create_team.html', {'event' : event_info, 'logged': True, 'wrongpwd': False, 'registered' : True, 'notexist' : False})
members = EventPlayer.objects.filter(team=player.team, event=event_info)
if request.method == 'POST':
tname = player.team.name
p_form = TeamUpdateForm(request.POST, instance=player.team)
error = None
success = None
if p_form.is_valid():
pname = p_form.cleaned_data['name']
if pname == tname:
pass
else:
if Team.objects.filter(name=pname, event=event_info).exists():
error = _("Name already taken.")
ppassword = p_form.cleaned_data['password']
if error is None:
p_form.save()
success = _("Updated.")
context={'p_form': p_form, 'error':error, 'success' : success, 'player':player, 'members':members}
return render(request, 'events/manage_team.html', context)
else:
p_form = TeamUpdateForm(instance=player.team)
context={'p_form': p_form, 'player':player, 'members':members}
return render(request, 'events/manage_team.html',context)
@login_required
def leave_team(request, event_slug):
event_info = get_object_or_404(Event, slug=event_slug)
player = EventPlayer.objects.get(user=request.user, event=event_info)
team = Team.objects.get(event=event_info, name=player.team.name)
team.score -= player.score
team.save()
player.team = None
solved = CTF_flags.objects.filter(user=player.user, ctf__event=event_info)
player.score = 0
solved.delete()
player.save()
members = EventPlayer.objects.filter(team=team, event=event_info)
if members.count() == 0:
team.delete()
return redirect('events:event_info', event_slug=event_slug)
@login_required
def find_team(request, event_slug):
event_info = get_object_or_404(Event, slug=event_slug)
teams = Team.objects.filter(event=event_info, auto=True)
team = None
player = EventPlayer.objects.get(user=request.user, event=event_info)
if event_info.auto_match == False:
return redirect('events:event_info', event_slug=event_slug)
for t in teams:
if EventPlayer.objects.filter(team=t, event=event_info).count() < event_info.team_size:
team = t
break
if team is None:
teamname = get_random_name()
while Team.objects.filter(name=teamname, event=event_info).exists():
teamname = get_random_name()
team = Team(name=teamname, password="".join([str(randint(0,10)) for _ in range(16)]), event=event_info, auto=True)
team.save()
player.team = team
player.save()
return redirect('events:event_info', event_slug=event_slug)
@login_required
def open_team(request, event_slug):
event_info = get_object_or_404(Event, slug=event_slug)
player = EventPlayer.objects.get(user=request.user, event=event_info)
if not player.team:
return render(request, 'events/create_team.html', {'event' : event_info, 'logged': True, 'wrongpwd': False, 'registered' : True, 'notexist' : False})
player.team.auto = True
player.team.save()
return redirect('events:manage_team', event_slug=event_slug)
@login_required
def close_team(request, event_slug):
event_info = get_object_or_404(Event, slug=event_slug)
player = EventPlayer.objects.get(user=request.user, event=event_info)
if not player.team:
return render(request, 'events/create_team.html', {'event' : event_info, 'logged': True, 'wrongpwd': False, 'registered' : True, 'notexist' : False})
player.team.auto = False
player.team.save()
return redirect('events:manage_team', event_slug=event_slug)

View File

@ -0,0 +1,33 @@
# Generated by Django 3.2.7 on 2021-09-07 19:12
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('home', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='new',
name='content_de',
field=models.TextField(blank=True),
),
migrations.AddField(
model_name='new',
name='content_en',
field=models.TextField(blank=True),
),
migrations.AddField(
model_name='new',
name='content_ru',
field=models.TextField(blank=True),
),
migrations.AlterField(
model_name='new',
name='content',
field=models.TextField(blank=True),
),
]

View File

@ -2,7 +2,10 @@ from django.db import models
class new(models.Model):
name = models.CharField(max_length=100)
content = models.TextField()
content = models.TextField(blank=True)
content_en = models.TextField(blank=True)
content_ru = models.TextField(blank=True)
content_de = models.TextField(blank=True)
slug = models.SlugField(max_length=55)
pub_date = models.DateTimeField('Date published')
def __str__(self):

View File

@ -0,0 +1,68 @@
{% extends 'base.html' %}
{% block content %}
{% load i18n %}
{% get_current_language as lang %}
<div class="row">
<div class="col-12 news-card">
<h2>Conditions générales d'utilisation du site 42CTF</h2>
<h5>Article 1 : Objet</h5>
Les présentes CGU ou Conditions Générales dUtilisation encadrent juridiquement lutilisation des services du site 42CTF (ci-après dénommé « le site »). <br><br>
Constituant le contrat entre les éditeurs de 42CTF et lUtilisateur, laccès au site doit être précédé de lacceptation de ces CGU. Laccès à cette plateforme signifie lacceptation des présentes CGU. <br><br>
<h5>Article 2 : Mentions légales</h5>
Lhébergeur du site 42ctf.org est la société Google LLC, sise au 1600 Amphitheatre Parkway à Mountain View, États Unis.<br><br>
<h5>Article 3 : Accès au site</h5>
Le site 42CTF permet daccéder gratuitement aux services suivants : <br>
- challenges en sécurité informatique disponibles de manière permanente<br>
- compétitions en sécurité informatique à durée limitée<br><br>
Le site est accessible gratuitement depuis nimporte où par tout utilisateur disposant dun accès à Internet. Tous les frais nécessaires pour laccès aux services (matériel informatique, connexion Internet…) sont à la charge de lutilisateur.<br>
Laccès aux services dédiés aux membres seffectue à laide dun identifiant et dun mot de passe.<br>
Pour des raisons de maintenance ou autres, laccès au site peut être interrompu ou suspendu par léditeur sans préavis ni justification.<br><br>
<h5>Article 4 : Collecte des données</h5>
Pour la création du compte de lUtilisateur, la collecte des informations au moment de linscription sur le site est nécessaire et obligatoire. Conformément à la loi n°78-17 du 6 janvier relative à linformatique, aux fichiers et aux libertés, la collecte et le traitement dinformations personnelles seffectuent dans le respect de la vie privée.<br>
Suivant la loi Informatique et Libertés en date du 6 janvier 1978, articles 39 et 40, lUtilisateur dispose du droit daccéder, de rectifier, de supprimer et dopposer ses données personnelles. Lexercice de ce droit seffectue par :<br>
- Son espace utilisateur ;<br>
- Demande écrite par mail à 42ctf@protonmail.com .<br><br>
<h5>Article 5 : Propriété intellectuelle</h5>
Les marques, logos ainsi que les contenus du site 42CTF (illustrations graphiques, textes…) sont protégés par le Code de la propriété intellectuelle et par le droit dauteur.<br>
La reproduction et la copie des contenus par lUtilisateur requièrent une autorisation préalable du site. Dans ce cas, toute utilisation à des usages commerciaux ou à des fins publicitaires est proscrite.<br><br>
<h5>Article 6 : Responsabilité</h5>
Bien que les informations publiées sur le site soient réputées fiables, le site se réserve la faculté dune non-garantie de la fiabilité des sources.<br>
Les informations diffusées sur le site 42CTF sont présentées à titre purement informatif et sont sans valeur contractuelle. En dépit des mises à jour régulières, la responsabilité du site ne peut être engagée en cas de modification des dispositions administratives et juridiques apparaissant après la publication. Il en est de même pour lutilisation et linterprétation des informations communiquées sur la plateforme.<br><br>
Le site décline toute responsabilité concernant les éventuels virus pouvant infecter le matériel informatique de lUtilisateur après lutilisation ou laccès à ce site.
Le site ne peut être tenu pour responsable en cas de force majeure ou du fait imprévisible et insurmontable dun tiers.<br>
La garantie totale de la sécurité et la confidentialité des données nest pas assurée par le site. Cependant, le site sengage à mettre en œuvre toutes les méthodes requises pour le faire au mieux.<br><br>
<h5>Article 7 : Liens hypertextes</h5>
Le site peut être constitué de liens hypertextes. En cliquant sur ces derniers, lUtilisateur sortira de la plateforme. Cette dernière na pas de contrôle et ne peut pas être tenue responsable du contenu des pages web relatives à ces liens.<br><br>
<h5>Article 8 : Cookies</h5>
En naviguant sur le site, lUtilisateur accepte les cookies. <br><br>
<h5>Article 9 : Durée du contrat</h5>
Le présent contrat est valable pour une durée indéterminée. Le début de lutilisation des services du site marque lapplication du contrat à légard de lUtilisateur.<br><br>
<h5>Article 10 : Droit applicable et juridiction compétente</h5>
Le présent contrat est soumis à la législation française. Labsence de résolution à lamiable des cas de litige entre les parties implique le recours aux tribunaux français compétents pour régler le contentieux.
</div>
</div>
{% endblock %}

View File

@ -1,14 +1,43 @@
{% extends 'base.html' %}
{% block content %}
{% load i18n %}
{% get_current_language as lang %}
{% load is_member %}
{% load get_news %}
<div class="row">
<div class="col-lg-9 col-sm-12 news-card">
<div class="col-lg-3 col-sm-12 right-sidebar middle-sm">
<ul class="list-group">
<li class="list-group-item">Top 10</li>
{% for t in top %}
{% ismember t.user.userprofileinfo as is_member %}
<li class="list-group-item text-truncate"># {{ forloop.counter }}
<a class="profile_link {{is_member}}" href="{% url 'accounts:profile' user_name=t.user.username %}"> {{ t.user.username }}</a>
<span style="position:absolute;right: 15px;">{{ t.score }}</span></li>
{% endfor %}
</ul>
<ul class="list-group">
<li class="list-group-item">{% trans "Weekly Top 5" %}</li>
{% for user, score in top_weekly %}
{% ismember user as is_member %}
<li class="list-group-item text-truncate"># {{ forloop.counter }}
<a class="profile_link {{is_member}}" href="{% url 'accounts:profile' user_name=user %}"> {{ user }}</a>
<span style="position:absolute;right: 15px;">{{ score }}</span></li>
{% endfor %}
</ul>
</div>
<div class="col-lg-6 col-sm-12 news-card top-sm">
{% if news %}
{% for n in news %}
<div class="card text-center news-card">
<div class="card-body">
<h5 class="card-title">{{ n.name|safe }}</h5>
<p class="card-text">{{ n.content|safe }}</p>
<p class="card-text">
{% get_news_by_lang n lang as content %}
{{ content | safe }}
</p>
</div>
<div class="card-footer text-muted">
{{ n.pub_date }}
@ -19,27 +48,46 @@
<p class="text-center">{% trans "No article available." %}</p>
{% endif %}
</div>
<div class="col-lg-3 col-sm-12 right-sidebar">
<div class="col-lg-3 col-sm-12 right-sidebar bottom-sm">
<ul class="list-group">
<li class="list-group-item active">{% trans "Latest challenges added" %}</li>
{% if ctfs %}
{% for ctf in ctfs %}
<a class="list-group-item" href="/ctfs/{{ ctf.category.slug }}/{{ ctf.slug }}">{{ ctf.name }}</a>
<li class="list-group-item">
<a class="ctf-link" href="{% url 'ctf' cat_slug=ctf.category.slug ctf_slug=ctf.slug %}">{{ ctf.name }}</a>
<span style="position:absolute;right: 15px;">{{ctf.points}} {% trans "points" %}</span>
</li>
{% endfor %}
{% else %}
<li class="list-group-item">{% trans "No ctf available." %}</li>
<li class="list-group-item">{% trans "No ctf available." %}</li>
{% endif %}
</ul>
<ul class="list-group">
<li class="list-group-item active">Top 10</li>
{% for t in top %}
<li class="list-group-item"># {{ forloop.counter }} <a class="profile_link" href="/accounts/profile/{{ t.user.username }}"> {{ t.user.username }}</a> <span style="position:absolute;right: 15px;">{{ t.score }}</span></li>
<li class="list-group-item">{% trans "Latest Flags" %}</li>
{% for f in latest_flags %}
{% ismember f.user.userprofileinfo as is_member %}
<li class="list-group-item text-truncate">
<a class="ctf-link" href="{% url 'ctf' cat_slug=f.ctf.category.slug ctf_slug=f.ctf.slug %}"> {{f.ctf}}</a>
<span style="position:absolute;right: 15px;">
<a class="profile_link {{is_member}}" href="{% url 'accounts:profile' user_name=f.user.username %}">{{ f.user.username }}</a>
</span>
</li>
{% endfor %}
</ul>
<div class="row flex-nowrap">
<div class="col-lg-6 col-md-6">
<ul class="list-group">
<li class="list-group-item active">{% trans "Flags counter" %}</li>
<li class="list-group-item active">{% trans "Flags" %}</li>
<li class="list-group-item"><span>{{ flags }}</span></li>
</ul>
</div>
<div class="col-lg-6 col-md-6">
<ul class="list-group">
<li class="list-group-item active">{% trans "Users" %}</li>
<li class="list-group-item"><span>{{ nb_users }}</span></li>
</ul>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,4 @@
Wie Sie es vielleicht schon wissen, braucht man um CTF Herausforderungen zu lösen viele Werkzeuge und es ist manchmal schwierig zu wissen welche benötigt werden.</br>
Wir haben eine VM erstellt, mit vorinstallierten Werkzeugen, damit Sie sich auf das wesentliche konzentrieren können: Flaggen!</br>
Alles was Sie tun müssen ist diese <b><a href="/media/xubuntu-42ctf.ova">OVA</a></b> herunterzuladen und auf <b><a href="https://www.virtualbox.org/wiki/Downloads">Virtual Box</a></b> zu importieren.<br>
Also, worauf warten Sie?

View File

@ -0,0 +1,7 @@
Haben Sie eine Änderung am Punktestand bemerkt?<br><br>
Keine Panik, alle Ihre Flaggen sind in Sicherheit. Wir haben bloß zu dynamischen Belohnungen gewächselt. Das heißt, dass die Punktzahl der Herausforderungen nicht mehr festgelegt ist: sie sinken nun jedes Mal, dass die Herausforderung gelöst wird.
Belohnungspunkte beginnen bei 200 und können nicht unter 5 fallen.<br><br>
Wir erhoffen, dass dadurch die echte Schwierigkeit der Herausforderungen besser gespiegelt werden kann. Zeitgebundene Ereignisse sind von dieser Änderung nicht beinträchtigt.

View File

@ -0,0 +1,9 @@
Suchen Sie Ihren Seelenverwandten, einen neuen Freund oder bloß einen dezenten CTF-Partner?<br><br>
Wir bei 42CTF haben was Sie brauchen: den <a href="/events/speed_dating_2022">Speed Dating CTF</a>!<br><br>
Kommen Sie alleine oder gut begleitet zu diesem sehr kurzen Wettbewerb, der nur 4 Studen dauern wird.<br>
Für dieses Team-CTF können Sie nur auf einen anderen Spieler zählen.<br>
Sie können entweder Ihren Partner aussuchen oder das Schicksal für sie entscheiden lassen.<br><br>
Viel Glück!

View File

@ -0,0 +1,8 @@
Schon immer lust gehabt etwas über SQL-Einbrüche zu lernen?<br>
<br>
Wir bieten Ihnen drei brandneue Herausvorderungen erstellt von <b><a class=profile_link href=https://www.42ctf.org/accounts/profile/aldubar>aldubar</a></b>:<br>
- <b><a href='https://www.42ctf.org/ctfs/web/simple_question_1'>Simple Question of Logic 1</a></b> (10 Punkte)<br>
- <b><a href='https://www.42ctf.org/ctfs/web/simple_question_2'>Simple Question of Logic 2</a></b> (30 Punkte)<br>
- <b><a href='https://www.42ctf.org/ctfs/web/simple_question_3'>Simple Question of Logic 3</a></b> (40 Punkte)<br>
<br>
Vergessen Sie nicht, dass Sie uns jederzeit auf <a class="footer_imgs" href="https://discord.gg/DwZqPpA" target="_blank"><img src="/static/img/discord.png" width="30"></a> erreichen können, um uns neue Herausforderungen vorzuschalgen!

View File

@ -0,0 +1,9 @@
Neues zeitgebundenes Ereignis: <b>Welcome CTF 2021</b>!<br><br>
Datum: vom 10.12.2021 20 Uhr biz zum 12.12.2021 20 Uhr (Pariser Zeit).<br>
Es ist ein CTF um die Studenten die neulich 42 beigetreten haben zu begrüßen.<br>
Es wird nur für die Personen die ihren Kursus <b>nach</b> dem 01.09.2021 begonnen haben zugänglich sein.<br><br>
Ansonsten können Sie trotzdem die auf der Webseite bereits verfügbaren Herausforderungen lösen und versuchen das Top 10 zu erreichen!<br><br>
Registrieren Sie sich <a href=https://forms.42l.fr/apps/forms/SooTbnT4PCs9na7C>hier</a>.

View File

@ -0,0 +1,4 @@
As you may already be aware, solving CTF challenges require a lot of tools and it may be difficult to find out which ones you need to install. </br>
We've made a VM with everything you need for 42CTF challenges so that you can focus on what is really important: flag ! </br>
All you need to do is download this <b><a href="/media/xubuntu-42ctf.ova">OVA</a></b> and import it in <b><a href="https://www.virtualbox.org/wiki/Downloads">Virtual Box</a>.<br></b>
So, what are you waiting for ?

View File

@ -0,0 +1,7 @@
You noticed a little change in the 42CTF scoreboard ?<br><br>
Don't panic, all your flags are safe and sound. We just switched to dynamic scoring. It means that challenges points are not fixed anymore: they will now decrease with each solve.<br>
Challenges points start at 200, and cannot go below than 5.<br><br>
We hope that this will allow better reflecting of the actual difficulty of the challenges. Limited-time events are not affected by this change.

View File

@ -0,0 +1,9 @@
Looking for your soul mate, a new friend, or just a decent CTF companion ?<br><br>
We at 42CTF have what you need: the <a href="/events/speed_dating_2022">Speed Dating CTF</a> !<br><br>
Come alone or in good company for this very short competition, which will last only 4 hours.<br>
You will be able to count on only one other player for this team CTF.<br>
You can either choose your partner or you can let the fate decide for you.<br><br>
Good luck !

View File

@ -0,0 +1,8 @@
Always wanted to learn about SQL injections ? <br>
<br>
We offer you three brand new challenges created by <b><a class=profile_link href=https://www.42ctf.org/accounts/profile/aldubar>aldubar</a></b>:<br>
- <b><a href='https://www.42ctf.org/ctfs/web/simple_question_1'>Simple Question of Logic 1</a></b> (10 points)<br>
- <b><a href='https://www.42ctf.org/ctfs/web/simple_question_2'>Simple Question of Logic 2</a></b> (30 points)<br>
- <b><a href='https://www.42ctf.org/ctfs/web/simple_question_3'>Simple Question of Logic 3</a></b> (40 points)<br>
<br>
Don't forget that you can always reach out on <a class="footer_imgs" href="https://discord.gg/DwZqPpA" target="_blank"><img src="/static/img/discord.png" width="30"></a> to propose new challenges !

View File

@ -0,0 +1,9 @@
New limited-time event : <b>Welcome CTF 2021</b> !<br><br>
Dates: from 10/12/2021 8pm to 12/12/2021 8pm (Paris time).<br>
This is a CTF to welcome the new students who join us at 42.<br>
It will be accessible only for the persons who started their cursus <b>after</b> the 01/09/2021.<br><br>
For the others, you can still solve the challenges already available on the website and try to reach the top 10 ! <br><br>
Registration <a href=https://forms.42l.fr/apps/forms/SooTbnT4PCs9na7C>here</a>

View File

@ -0,0 +1,4 @@
Como ya sabrás, resolver retos CTF requiere un montón de herramientas y puede ser dificil encontrar cuales tienes que instalar. </br>
Hemos hecho una máquina virtual con todo lo que necesitas para resolver para resolver retos de 42CTF, para que te puedas concentrar en lo importante: Flags! </br>
Todo lo que necesitas es descargar esto <b><a href="/media/xubuntu-42ctf.ova">OVA</a></b> e importarlo en <b><a href="https://www.virtualbox.org/wiki/Downloads">Virtual Box</a>.<br></b>
¿A qué estás esperando?

View File

@ -0,0 +1,7 @@
¿ Has notado un pequeño cambio en la Tabla de Puntos de 42CTF ?<br><br>
No entres en pánico, todas tus flags estan a salvo. Solo hemos cambiado a puntuación dinámica. Esto significa que los puntos de retos no son fijos: irán disminuyendo cada vez que son resueltos.<br>
Los puntos de retos empiezan en 200, y no pueden valer menos de 5 puntos. <br><br>
Esperamos que esto ayude a reflejar la dificultad del reto. Eventos de tiempo limitado nos e ven afectados por este cambio.

View File

@ -0,0 +1,9 @@
¿ Estás buscando tu alma gemela ? ¿ Un nuevo amigo ? ¿ O simplemente un buen compañero de CTF ?<br><br>
Aquí en 42CTF tenemos lo que necesitas: el <a href="/events/speed_dating_2022">Speed Dating CTF</a> !<br><br>
Ven solo o en compañía a esta competición cortita, que solo durará 4 horas.<br>
Podrás contar con un solo compañero para este CTF por equipos.<br>
Puedes o elegir un compañero o dejar a la fortuna que elija por ti.<br><br>
¡ Buena suerte !

View File

@ -0,0 +1,8 @@
¿ Siempre has querido aprender sobre SQL injection ? <br>
<br>
Te ofrecemos tres nuevos retos creados por <b><a class=profile_link href=https://www.42ctf.org/accounts/profile/aldubar>aldubar</a></b>:<br>
- <b><a href='https://www.42ctf.org/ctfs/web/simple_question_1'>Cuestión de lógica simple 1</a></b> (10 puntos)<br>
- <b><a href='https://www.42ctf.org/ctfs/web/simple_question_2'>Cuestión de lógica simple 2</a></b> (30 puntos)<br>
- <b><a href='https://www.42ctf.org/ctfs/web/simple_question_3'>Cuestión de lógica simple 3</a></b> (40 puntos)<br>
<br>
No te olvides que siempre puedes contactarnos en <a class="footer_imgs" href="https://discord.gg/DwZqPpA" target="_blank"><img src="/static/img/discord.png" width="30"></a> para proponer nuevos retos !

View File

@ -0,0 +1,9 @@
Nuevo evento de tiempo limitado: <b>Bienvenida a CTF 2021</b> !<br><br>
Fechas: desde 10/12/2021 8pm a 12/12/2021 8pm (Hora parís).<br>
Este CTF es una bienvenida para los nuevos estudiantes que se unan a 42.<br>
Solo estará disponible para las personas que empezaron su cursus <b>después</b> de 01/09/2021.<br><br>
Para otros, puedes resolver otros retos ya disponibles e intentar llegar al top 10! <br><br>
Registro: <a href=https://forms.42l.fr/apps/forms/SooTbnT4PCs9na7C>aquí</a>

View File

@ -0,0 +1,7 @@
Comme vous le savez surement déjà, résoudre des challenges nécessite beaucoup d'outils et il peut être difficile de savoir lesquels sont vraiment nécessaires. <br>
Nous avons créé une VM avec tout ce dont vous avez besoin pour 42CTF afin que vous vous concentriez sur ce qui compte vraiment : flag ! <br>
Tout ce dont vous avez besoin est de télécharger cet <b><a href="/media/xubuntu-42ctf.ova">OVA</a></b> et de l'importer dans <b><a href="https://www.virtualbox.org/wiki/Downloads">Virtual Box</a></b>.<br>
Alors, qu'est ce que vous attendez ?

View File

@ -0,0 +1,7 @@
Vous avez remarqué un petit changement sur le scoreboard de 42CTF ?<br><br>
Pas de panique, tous vos flags sont sains et saufs. Nous avons juste basculé sur du scoring dynamique. Cela signifie que les points des challenges ne sont plus fixes : ils diminueront désormais à chaque nouvelle résolution.<br>
Un challenge démarre à 200 points, et ne peut pas descendre en dessous de 5 points.<br><br>
Nous espérons que cela permettra de mieux refléter la véritable difficulté des challenges. Les événements à durée limitée ne sont pas concernés par ce changement.

View File

@ -0,0 +1,9 @@
À la recherche de l'âme soeur, d'un nouvel ami, ou juste d'un compagnon de CTF ?<br><br>
Nous avons ce dont vous avez besoin : le <a href="/events/speed_dating_2022">Speed Dating CTF</a> !<br><br>
Venez seul ou bien accompagné pour cette très courte compétition, qui ne durera que 4 heures.<br>
Vous ne pourrez compter que sur une seule autre personne pour ce CTF en équipe.<br>
Vous pouvez soit choisir votre partenaire, soit laisser laisser le destin décider pour vous.<br><br>
Bonne chance !

View File

@ -0,0 +1,8 @@
Vous avez toujours voulu en apprendre plus sur les injections SQL ? <br>
<br>
On vous propose trois nouveaux challenges créés par <b><a class=profile_link href=https://www.42ctf.org/accounts/profile/aldubar>aldubar</a></b>:<br>
- <b><a href='https://www.42ctf.org/ctfs/web/simple_question_1'>Simple Question of Logic 1</a></b> (10 points)<br>
- <b><a href='https://www.42ctf.org/ctfs/web/simple_question_2'>Simple Question of Logic 2</a></b> (30 points)<br>
- <b><a href='https://www.42ctf.org/ctfs/web/simple_question_3'>Simple Question of Logic 3</a></b> (40 points)<br>
<br>
N'oubliez pas que vous pouvez toujours nous contacter sur <a class="footer_imgs" href="https://discord.gg/DwZqPpA" target="_blank"><img src="/static/img/discord.png" width="30"></a> pour proposer des nouveaux challenges !

View File

@ -0,0 +1,9 @@
Nouvel évènement en temps limité : <b>Welcome CTF 2021</b> !<br><br>
Dates : du 10/12/2021 20h au 12/12/2021 20h. <br>
Il s'agit d'un CTF pour souhaiter la bienvenue aux nouveaux étudiants qui nous rejoignent à 42.<br>
Il ne sera donc accessible qu'aux personnes ayant effectué leur rentrée <b>après</b> le 01/09/2021.<br><br>
Pour les autres, vous pouvez toujours résoudre les challenges déjà disponibles sur le site et tenter de vous hisser dans le top 10 ! <br><br>
Inscriptions <a href=https://forms.42l.fr/apps/forms/SooTbnT4PCs9na7C>ici</a>

View File

@ -0,0 +1,4 @@
すでにお気づきかもしれませんが、CTFの課題を解くには多くのツールが必要で、どれをインストールすれば良いのかがわかりにくいかもしれません。</br>
私たちは、あなたが本当に重要なことに集中できるように、42CTFの課題に必要な全てのツールを備えたVMを作成しました。重要なのはフラグです</br>
この<b><a href="/media/xubuntu-42ctf.ova">OVA</a></b>をダウンロードし、<b><a href="https://www.virtualbox.org/wiki/Downloads">Virtual Box</a></b>にインポートするだけです。<br>
さて、何をためらっているのですか?

Some files were not shown because too many files have changed in this diff Show More