Skip to content

Commit

Permalink
Merge pull request #157 from TFNS/dev
Browse files Browse the repository at this point in the history
v2.1.0
  • Loading branch information
JJ-8 authored Apr 22, 2022
2 parents c125a07 + c055d3e commit 71e183e
Show file tree
Hide file tree
Showing 31 changed files with 5,456 additions and 2,855 deletions.
92 changes: 92 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# CTFNote - Contribution Guide

When contributing to this repository, please first discuss the change you wish to make via issue with the collaborators of this repository before making a change.

## Git process

In order to push new code on this repository, you first have to create a new fork within your github workspace.

Once your fork is created (`https://github.com/<your_username>/CTFNote`), you can create a new branch starting with the associated issue number in the name and start working on it.

> If you start from scratch and no issues are associated with your changes, you can create a branch starting with '0-'.
```shell
$ git checkout -b <issue-number>-<branch-name>
```

Examples of branch name:

- `132-add-new-feature`
- `343-add-past-ctf-role`
- `13-fix-bug-in-password-reset`
- `37-leak-flag-to-TFNS`
- `0-contribution-guide`

Once you think the job is done, issue the pull request and target the **dev** branch of the official CTFNote repository.

You can also create the pull request before finishing the job but don't forget to add "WiP: " as a suffix in the title to let the collaborators know you are still working on the request.

## Deploying the dev version

### Prerequisites

- [Install docker](https://docs.docker.com/get-docker/)
- [Install yarn](https://classic.yarnpkg.com/lang/en/docs/install/)

### Install the git hooks

Go at the root folder and install the dependencies and install the git hooks:

```shell
$ yarn
```

This should run the prepare script and install the linting pre-commit hooks:

```
[1/4] 🔍 Resolving packages...
[2/4] 🚚 Fetching packages...
[3/4] 🔗 Linking dependencies...
[4/4] 🔨 Building fresh packages...
$ husky install
[##] 2/2husky - Git hooks installed
✨ Done in 0.40s.
```

### Start the third party containers

```shell
$ docker compose \
-f docker-compose.dev.yml \
up -d hedgedoc db adminer
```

### Start the API

```shell
$ cd api
api/ $ yarn # Install the dependencies
api/ $ yarn dev # Run the dev version (hot reloading included)
```

### Start the Front

```shell
$ cd front
front/ $ yarn # Install the dependencies
front/ $ yarn dev # Run the dev version (hot reloading included)
```

### Exposed endpoints

The following endpoint are exposed and can be used in the developpement environment

- [API](http://localhost:3000/)
- [GraphiQL](http://localhost:3000/graphiql)
- [Hedgedoc](http://localhost:3001/)
- [Quasar APP](http://localhost:8088/)
- [Adminer](http://localhost:3002/?pgsql=db&username=ctfnote&db=ctfnote)

## Review

To merge a pull request, two distinct reviews from two different collaborators are required.
37 changes: 27 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
[![CTFNote logo](screenshots/logo_small.webp)](screenshots/logo.png)

# CTFNote

## Introduction

CTFNote is a collaborative tool aiming to help CTF teams to organise their work.

[![Screenshot of the task list](screenshots/task_small.webp)](screenshots/task.png)


## Installation

Before starting, make sure to fill in the information in the `.env` file.
Expand Down Expand Up @@ -52,12 +53,14 @@ server {

Edit the `docker-compose.yml` file to make sure CTFNote only listens on
localhost:

```diff
- - 8080:80
+ - 127.0.0.1:8080:80
```

Edit the `.env` file to instruct the pad to use TLS:

```diff
# Secure: we're using HTTPS
-# CMD_PROTOCOL_USESSL=true
Expand All @@ -68,48 +71,52 @@ Edit the `.env` file to instruct the pad to use TLS:
+CMD_DOMAIN=example.org:1337
```

After deploying this configuration, run `certbot` to make it available over HTTPS.
After deploying this configuration, run `certbot` to make it available over HTTPS.
See [this article](https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-20-04) for more information.

### Migration

If you already have an instance of CTFNote in a previous version and wish to
upgrade, you should follow the guide at [MIGRATION.md](MIGRATION.md).


## Privileges

When other players register on your CTFNote instance, they will not be able to
see CTF or tasks. This is because CTFNote uses different roles to restrict CTF

You can manage other players' roles in the *Users* tab of the *Admin* panel.
You can manage other players' roles in the _Users_ tab of the _Admin_ panel.

Additionally, you can generate a secret that lets users create an account with a
different privilege in the *Registration with password* menu in the *Admin*
different privilege in the _Registration with password_ menu in the _Admin_
panel.

![Screenshot of the Registration with password menu](screenshots/reg_password.png)

### Guest

Guest is the default role. This role is meant to be used for guests and friends
helping sporadically on CTF.

You can add a guest to a CTF by ticking their badge in the *Guests* tab on a
You can add a guest to a CTF by ticking their badge in the _Guests_ tab on a
specific CTF.

![Screenshot of the guest menu](screenshots/guests.png)

### Friend
Friend is a role between guest and member which allows the player to automatically

Friend is a role between guest and member which allows the player to automatically
view old CTFs but not active and upcoming CTFs. They are also not allowed to invite any
new players to a CTF. You can use this role to grant guests access to your old CTFs
for them to learn from without granting access to each old CTF individually.

### Member

Member is a role that represents a team member. A certain level of trust is
given to these users: they can see every CTF, future, current and past. They can
also invite guests to CTF.

### Manager

Manager is a role that represents a team captain. They can create, import,
modify and delete CTF.

Expand All @@ -118,32 +125,42 @@ They can import CTF directly from [CTFtime](https://ctftime.org).
![Screenshot of the Import CTF feature](screenshots/import.png)

### Admin
Admin is a role with every privileges. They have access to the *Admin* panel

Admin is a role with every privileges. They have access to the _Admin_ panel
that lets them delete accounts, change permissions, reset passwords, create
one-time secrets and, most importantly, change the theme colours.

![Screenshot of the theme menu](screenshots/theme.png)


## Configuration

The configuration can be changed in the `.env` file. This file contains
environment variables for the containers.

The value of every variables are explained in this file.


## Screenshots

### List of the CTF

[![Screenshot of the CTF page](screenshots/ctf_small.webp)](screenshots/ctf.png)

### Calendar

[![Screenshot of the CTF calendar](screenshots/calendar_small.webp)](screenshots/calendar.png)

### Information of a single CTF

[![Screenshot of the CTF info](screenshots/info_small.webp)](screenshots/info.png)

### Task list for a CTF

[![Screenshot of the task list](screenshots/task_small.webp)](screenshots/task.png)

### Shared notepad for a task

[![Screenshot of pad](screenshots/pad_small.webp)](screenshots/pad.png)

## Contributing

A contribution guide is available here: [CONTRIBUTING.md](CONTRIBUTING.md)
2 changes: 2 additions & 0 deletions api/migrations/40-search-tasks-n-ctfs.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CREATE INDEX ON ctfnote.ctf (title);
CREATE INDEX ON ctfnote.task (title);
6 changes: 3 additions & 3 deletions api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"start": "NODE_ENV=production node dist/index.js",
"build": "tsc",
"lint": "eslint --fix 'src/**/*.ts'",
"format": "prettier --write 'src/*.ts'",
"format": "prettier --write 'src/**/*.ts'",
"dev": "NODE_ENV=development nodemon src/index.ts",
"dev:migrate": "DATABASE_URL= yarn run db-migrate -e dev up"
},
Expand All @@ -27,8 +27,8 @@
"graphile-utils": "^4.11.2",
"graphql": "^15.6.1",
"graphql-upload": "^12.0.0",
"postgraphile": "^4.11.0",
"postgraphile-plugin-connection-filter": "^2.1.1",
"postgraphile": "^4.12.8",
"postgraphile-plugin-connection-filter": "^2.2.2",
"postgres-migrations": "^5.3.0"
},
"devDependencies": {
Expand Down
12 changes: 11 additions & 1 deletion api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import createTasKPlugin from "./plugins/createTask";
import importCtfPlugin from "./plugins/importCtf";
import uploadLogoPlugin from "./plugins/uploadLogo";
import uploadScalar from "./plugins/uploadScalar";
import ConnectionFilterPlugin from "postgraphile-plugin-connection-filter";

function getDbUrl(role: "user" | "admin") {
const login = config.db[role].login;
Expand Down Expand Up @@ -43,6 +44,7 @@ function createOptions() {
importCtfPlugin,
uploadLogoPlugin,
createTasKPlugin,
ConnectionFilterPlugin,
],
ownerConnectionString: getDbUrl("admin"),
enableQueryBatching: true,
Expand All @@ -60,6 +62,14 @@ function createOptions() {
postgraphileOptions.jwtSecret = "DEV";
postgraphileOptions.showErrorStack = "json" as const;
postgraphileOptions.extendedErrors = ["hint", "detail", "errcode"];

postgraphileOptions.graphileBuildOptions = {
connectionFilterAllowedOperators: ["includesInsensitive"],
connectionFilterAllowedFieldTypes: ["String"],
connectionFilterComputedColumns: false,
connectionFilterSetofFunctions: false,
connectionFilterArrays: false,
};
}
return postgraphileOptions;
}
Expand All @@ -70,7 +80,7 @@ function createApp(postgraphileOptions: PostGraphileOptions) {
app.use(
"/uploads",
express.static("uploads", {
setHeaders: function (res, path, stat) {
setHeaders: function (res) {
res.set("Content-Disposition", "attachment");
},
})
Expand Down
62 changes: 56 additions & 6 deletions api/src/plugins/createTask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,52 @@ import axios from "axios";
import savepointWrapper from "./savepointWrapper";
import config from "../config";

async function createPad(): Promise<string> {
function buildNoteContent(
title: string,
description?: string,
category?: string
): string {
let note = "";

note += `# ${title}`;

if (category) {
note += ` - ${category}`;
}

note += "\n\n";

if (description) {
note += `## Description\n`;
note += "\n";
note += `${description}\n`;

note += "\n";
note += "----\n";
}
return note;
}

async function createPad(
title: string,
description?: string,
category?: string
): Promise<string> {
const options = {
headers: {
"Content-Type": "text/markdown",
},

maxRedirects: 0,
validateStatus: (status: number) => status === 302,
};

try {
const res = await axios.get(config.pad.createUrl, {
maxRedirects: 0,
validateStatus: (status) => status === 302,
});
const res = await axios.post(
config.pad.createUrl,
buildNoteContent(title, description, category),
options
);
return res.headers.location;
} catch (e) {
throw Error(`Call to ${config.pad.createUrl} during task creation failed.`);
Expand Down Expand Up @@ -44,7 +84,17 @@ export default makeExtendSchemaPlugin((build) => {
{ pgClient },
resolveInfo
) => {
const padPathOrUrl = await createPad();
const {
rows: [isAllowed],
} = await pgClient.query(`SELECT ctfnote_private.can_play_ctf($1)`, [
ctfId,
]);

if (isAllowed.can_play_ctf !== true) {
return {};
}

const padPathOrUrl = await createPad(title, description, category);

let padPath: string;
if (padPathOrUrl.startsWith("/")) {
Expand Down
Loading

0 comments on commit 71e183e

Please sign in to comment.