Backend Documentation

[TOC]

Authentication & Authorization

Database

We have splitted authentication and authorization data in the database. The new users table no longer consists of the old role column. The user_group table has a new permission column which replaces the old role column.

Separating authentication and authorization is crucial to future integration of other services from the former esystem. We plan to add SSO for a centralized authentication, and service-specific user_group tables for separated authorization.

JWT Tokens

We use djangorestframework-simplejwt for JWT authentication.

Token Types

  • Access Token: Short-lived (30 minutes), sent in request header as Authorization: Bearer <token>

  • Refresh Token: Long-lived (7 days), stored in HttpOnly cookie

Token Storage

The refresh token is stored in an HttpOnly cookie (refresh_token) to prevent XSS attacks. The cookie settings are:

  • HttpOnly: Always enabled (not accessible via JavaScript)

  • Secure: Enabled in production (HTTPS only)

  • SameSite: Lax

  • Path: /api/v1/auth/ (only sent to auth endpoints)

Token Rotation

In production, refresh tokens are rotated on each refresh request. The old token is blacklisted and a new one is issued. In development, tokens are not rotated for easier debugging.

Authentication Flow

  1. User logs in via POST /api/v1/auth/token/ with username and password

  2. Server returns access token in response body and sets refresh token in HttpOnly cookie

  3. Client includes access token in Authorization header for subsequent requests

  4. When access token expires, client calls POST /api/v1/auth/token/refresh/ (cookie is sent automatically)

  5. Server returns new access token (and rotates refresh token in production)

  6. On logout, POST /api/v1/auth/logout/ blacklists the refresh token and clears the cookie

CSpace Authorization Rules

Group Management

There is an admin group named cspace. All the other groups, whether from LDAP or not, are all normal groups.

There are two roles, admin and member, in all groups.

  • normal - admin: can add / remove other users to / from the same group

  • normal - member: no permissions

  • cspace - admin: can add / remove other users to / from any group

  • cspace - member: can add /remove other users to / from any group except for the cspace group

Additionally, cspace group members can create or delete groups.

Registration

All users from normal groups can view / make / modify / remove registrations for their own group. All users from the cspace group can view / make / modify / remove registrations for any group.

Superuser

There is one single superuser ta217 that is the admin of the cspace group. It will be added by the entrypoint.sh that is executed when the container starts.

Docker

Dev

We mount the whole directory under /app so we can reflect changes of the db to local computer for better debugging.

The database backend is SQLite (/tmp/db.sqlite3). It is in the /tmp directory in the container and it will be removed when the container exits.

The ldap container creates ldap users (username, password):

  • ta217, ta217

  • thisway, hachu

  • chdir, plateau

  • user1, pw1

  • user2, pw2

  • user100, pw100

The cspace group is created and ta217 is added to it by default when the container starts.

The list of default groups (name[, description]) created on startup are as below:

cspace
faculty, gidNumber = 100
lab208
lab301
...

The full list is at docs/default-groups.txt. The default groups, which are groups that had permissions to make registrations in the old system, are filtered from the original group list (by hand).

To start the development server:

  1. cd into cspace-backend

  2. create a file .env

DB_USER=[enter username]
DB_PASSWORD=[enter password]
DJANGO_SECRET_KEY=[enter anything e.g. your-secret-key]
  1. Then execute:

docker compose -f docker-compose.yml -f docker-compose.dev.yml build --no-cache
docker compose -f docker-compose.yml -f docker-compose.dev.yml up

To stop: cd into cspace-backend

docker compose -f docker-compose.yml -f docker-compose.dev.yml down

Production

We should not mount the whole directory under /app. Instead, we should COPY all the necessary files into the image when building.

To start the production server: cd into cspace-backend

docker compose build --no-cache
docker compose up

To stop: cd into cspace-backend

docker compose down

The .env file should also include

DJANGO_ALLOWED_HOSTS=[production machine ip],localhost,127.0.0.1

Note: We don’t create the cspace group and the ta217 user on startup. They are imported manually through the adminer interface.

Note: We don’t create default groups on startup, groups are imported manually from the old db. We should manually remove unused groups from the web interface during setup.

Cookies

SameSite=

Setting

Sent on cross-site POST?

Sent on link click?

Security

Strict

❌ No

❌ No

🔒 Highest

Lax

❌ No

✅ Yes

🔒 Recommended

None

✅ Yes

✅ Yes

⚠️ Requires CSRF protection

Database Tables & Columns Descriptions

users

User identity and login metadata (authentication identity is LDAP-backed).

  • id (PK): Internal user identifier.

  • username: Unique login/account name.

  • displayname: Human-readable display name.

  • last_login_time: Last successful login timestamp.

  • last_login_ip: Source IP for the last successful login.

  • created_at: Record creation timestamp.

  • updated_at: Record update timestamp.

groups

Authorization groups used for access control and registration ownership.

  • id (PK): Group identifier.

  • name: Group name (for example cspace, faculty).

  • description: Optional group description.

  • created_at: Record creation timestamp.

  • updated_at: Record update timestamp.

user_group

Many-to-many mapping between users and groups, including per-group permission.

  • id (PK): Membership record identifier.

  • user_id (FK -> users.id): Member user.

  • group_id (FK -> groups.id): Group the user belongs to.

  • permissions: Role in the group (admin or member).

  • created_at: Record creation timestamp.

  • updated_at: Record update timestamp.

classrooms

Classroom master data and bookability flags.

  • id (PK): Classroom identifier.

  • name: Classroom name.

  • capacity: Maximum number of people.

  • equipment: Equipment description (json).

  • floor: Floor number.

  • require_permission: Whether special permission is required to register.

  • is_deleted: Soft-delete flag.

  • version: Version number for optimistic locking/auditing.

  • is_valid: Validation/availability flag.

draw_events

Defines a draw/lottery window and its booking period.

  • id (PK): Draw event identifier.

  • event_start_time (Datetime): Start time of booking period.

  • event_end_time (Datetime): End time of booking period.

  • target_start_date: Start date of the allocation window.

  • target_end_date: End date of the allocation window.

  • is_completed: Whether draw processing has completed.

  • is_deleted: Soft-delete flag.

  • created_at: Record creation timestamp.

  • updated_at: Record update timestamp.

  • version: Version number for optimistic locking/auditing.

draw_registrations

Applications submitted into a draw event before final registration creation.

  • id (PK): Draw registration identifier.

  • draw_event_id (FK -> draw_events.id): Parent draw event.

  • classroom_id (FK -> classrooms.id): Requested classroom.

  • user_id (FK -> users.id): Applicant user.

  • group_id (FK -> groups.id): Applicant group.

  • is_selected: Whether selected by the draw.

  • start_time (integer): Requested start time.

    • Stored as number of seconds since start of the week (every Sunday midnight 00:00).

  • end_time (integer): Requested end time.

    • Stored as number of seconds since start of the week (every Sunday midnight 00:00).

  • importance: Priority/importance score.

  • description: Applicant note/details.

  • is_deleted: Soft-delete flag.

  • created_at: Record creation timestamp.

  • updated_at: Record update timestamp.

  • version: Version number for optimistic locking/auditing.

Although start_time and end_time is being stored as integers, which might seem a little bit weird, we decide to follow the same design as the old system for easier data migration. Also, registrations need not to be made from the start of a period to the end of a period. It can be from any time to any time. Moreover, this design allows for better extensibility of the system.

We have considered storing them as start_period, end_period, and day_of_week. But after thorough consideration, we decided to stay the same. The same follows for the table longterm_registrations below.

longterm_registrations

Recurring booking templates (for example weekly recurring reservations).

  • id (PK): Long-term registration identifier.

  • user_id (FK -> users.id): Owner user.

  • group_id (FK -> groups.id): Owner group.

  • classroom_id (FK -> classrooms.id): Target classroom.

  • start_date: Effective recurrence start date.

  • end_date: Effective recurrence end date.

  • start_time (Datetime): Start time.

  • end_time (Datetime): End time.

  • day_of_week (Chars): Day of week.

  • description: Notes/details.

  • created_at: Record creation timestamp.

  • updated_at: Record update timestamp.

  • version: Version number for optimistic locking/auditing.

Although start_time and end_time are stored as Datetime, we only need the time of the day from it.

registrations

Finalized booking records (single reservations), optionally linked to draw/long-term sources.

  • id (PK): Registration identifier.

  • user_id (FK -> users.id): Booking owner user.

  • group_id (FK -> groups.id): Booking owner group.

  • classroom_id (FK -> classrooms.id): Reserved classroom.

  • draw_registration_id (FK -> draw_registrations.id, nullable): Source draw registration.

  • longterm_id (FK -> longterm_registrations.id, nullable): Source long-term template.

  • start_time (Datetime): Reservation start timestamp.

  • end_time (Datetime): Reservation end timestamp.

  • importance: Priority/importance value.

  • description: Reservation note/details.

  • is_pending: Approval status flag (true = pending).

  • is_deleted: Soft-delete flag.

  • created_at: Record creation timestamp.

  • updated_at: Record update timestamp.

  • version: Version number for optimistic locking/auditing.

notifications

System notification content.

  • id (PK): Notification identifier.

  • description: Notification body/content.

  • created_at: Record creation timestamp.

  • updated_at: Record update timestamp.

notification_users

Per-user/group delivery state for notifications.

  • id (PK): Delivery-state record identifier.

  • notification_id (FK -> notifications.id): Notification reference.

  • user_id (FK -> users.id): Target user.

  • group_id (FK -> groups.id): Related group context.

  • archived: Whether archived by user.

  • new: Whether still marked as unread/new.

  • created_at: Record creation timestamp.

  • updated_at: Record update timestamp.

change_logs

Auditable service logs for actions/changes.

  • id (PK): Log record identifier.

  • user_id (FK -> users.id): Actor user.

  • group_id (FK -> groups.id): Actor group context.

  • service: Service/module name (for example login, groups).

  • subject: Log subject/category (for example ok, failed, add, remove).

  • log: Detailed log message/body.

  • created_at: Record creation timestamp.

  • updated_at: Record update timestamp.