152 lines
9.1 KiB
Markdown
152 lines
9.1 KiB
Markdown
# Learner Management System (LMS) Backend
|
|
|
|
This is the backend service for the Learner Management System, built with Rust using the Actix Web framework. It provides a RESTful API for the frontend application, handles business logic, interacts with a MariaDB database using SQLx, and implements secure PAKE SRP authentication. The application is designed to be run using Docker and Docker Compose.
|
|
|
|
## Features
|
|
|
|
* **RESTful API:** Provides endpoints for authentication, user profiles, admin functions, and potentially course/classroom management in the future.
|
|
* **Secure Authentication:** Implements Password-Authenticated Key Exchange (PAKE) using the Secure Remote Password (SRP-6a) protocol via the `srp` crate.
|
|
* **High Performance:** Built with Rust and Actix Web for excellent performance, memory safety, and concurrency.
|
|
* **Database Interaction:** Uses SQLx for asynchronous, compile-time checked SQL queries against MariaDB.
|
|
* **Database Schema Management:** Automatically creates database tables on initial startup if they don't exist.
|
|
* **Sample Data Population:** Populates essential data (e.g., courses, sample users without passwords) on first run.
|
|
* **Dockerized:** Fully containerized using Docker and Docker Compose for easy setup, consistent environments, and deployment.
|
|
* **Configuration:** Configurable via environment variables (`.env` file support).
|
|
* **Modular Structure:** Code organized into logical modules (handlers, db, models, errors, config).
|
|
* **Basic Logging:** Uses `env_logger` for request and application logging.
|
|
|
|
## Technologies Used
|
|
|
|
* **Rust** (Stable toolchain)
|
|
* **Actix Web** (v4): High-performance web framework.
|
|
* **SQLx:** Asynchronous SQL toolkit with compile-time query checking.
|
|
* **`srp` crate:** For SRP-6a protocol implementation.
|
|
* **MariaDB:** Relational database (run via Docker).
|
|
* **Docker & Docker Compose:** For containerization and orchestration.
|
|
* **`serde`:** For data serialization/deserialization (JSON).
|
|
* **`dotenv`:** For loading environment variables from `.env` files.
|
|
* **`env_logger`:** For flexible logging configuration.
|
|
* **`chrono`:** For date/time handling.
|
|
* **`hex`:** For encoding/decoding SRP values.
|
|
* **`uuid`:** For generating session tokens (current implementation).
|
|
|
|
## Prerequisites
|
|
|
|
* **Docker**
|
|
* **Docker Compose** (v1 or v2 syntax compatible with the `docker-compose.yml`)
|
|
* **Rust Toolchain** (Stable - only needed if building/running outside the provided Docker setup)
|
|
|
|
## Getting Started
|
|
|
|
1. **Clone the Repository:** Clone the repository containing *both* the `lms-backend` folder and the main `docker-compose.yml`.
|
|
```bash
|
|
git clone <your-main-repository-url>
|
|
cd <your-main-repository-directory> # Should contain lms-backend/ and docker-compose.yml
|
|
```
|
|
|
|
2. **Create Environment File:** In the **root directory** (the one containing `docker-compose.yml`), create a `.env` file to specify database credentials. You can copy the example below or rely on the defaults defined in `docker-compose.yml`.
|
|
```dotenv
|
|
# .env (in the root directory)
|
|
|
|
# MariaDB Credentials (used by docker-compose to init the DB)
|
|
MARIADB_ROOT_PASSWORD=supersecretroot
|
|
MARIADB_DATABASE=lms_db
|
|
MARIADB_USER=lms_user
|
|
MARIADB_PASSWORD=lms_password
|
|
|
|
# Backend Configuration (can override defaults)
|
|
# SERVER_ADDR=0.0.0.0:8080 # Already set in compose file
|
|
RUST_LOG=info,lms_backend=debug # Example: Set backend log level to debug
|
|
# SECRET_KEY=your_secret_for_jwt_if_used # Needed if switching to JWT sessions
|
|
```
|
|
|
|
3. **Build and Run using Docker Compose:** From the **root directory**, run:
|
|
```bash
|
|
docker-compose up --build -d
|
|
```
|
|
* `--build`: Forces rebuilding the backend image if code has changed.
|
|
* `-d`: Runs the containers in detached mode (in the background).
|
|
* The first run might take some time to download images and build the Rust application.
|
|
|
|
4. **Accessing the API:**
|
|
* The API should now be running and accessible at `http://localhost:8080`.
|
|
* Test the health check endpoint: `http://localhost:8080/api/health`. You should receive a JSON response indicating success.
|
|
|
|
5. **Stopping the Services:**
|
|
```bash
|
|
docker-compose down
|
|
```
|
|
|
|
## Project Structure (`lms-backend/src/`)
|
|
|
|
```
|
|
src/
|
|
├── main.rs # Entry point, server setup, middleware, routing
|
|
├── config.rs # Configuration loading (.env)
|
|
├── db.rs # Database logic (SQLx queries, migrations/setup, data population)
|
|
├── handlers.rs # Actix route handlers, business logic, API endpoint definitions
|
|
├── models.rs # Data structures (DB models, API request/response structs)
|
|
└── errors.rs # Custom error types and `ResponseError` implementation
|
|
```
|
|
|
|
## Configuration
|
|
|
|
Configuration is primarily handled via environment variables, loaded using the `dotenv` crate during startup within the container. Key variables:
|
|
|
|
* `DATABASE_URL`: The connection string for MariaDB (automatically constructed in `docker-compose.yml`).
|
|
* `SERVER_ADDR`: The address and port the Actix server binds to (e.g., `0.0.0.0:8080`).
|
|
* `RUST_LOG`: Controls logging verbosity (e.g., `info`, `debug`, `actix_web=warn,lms_backend=debug`).
|
|
* `SECRET_KEY`: **Required** if implementing JWT-based sessions in the future.
|
|
|
|
These variables are typically passed from the host's `.env` file (if present) or directly set in the `environment` section of the `backend` service in `docker-compose.yml`.
|
|
|
|
## API Endpoints
|
|
|
|
The following main API endpoints are implemented in `src/handlers.rs`:
|
|
|
|
* `GET /api/health`: Health check.
|
|
* `POST /api/auth/srp/start`: Initiates SRP login (provides salt, server ephemeral).
|
|
* `POST /api/auth/srp/verify`: Verifies client SRP proof, generates session token on success.
|
|
* `POST /api/auth/logout`: (Basic) Invalidates session token.
|
|
* `GET /api/profile/{user_id}`: Retrieves user profile information.
|
|
* `PUT /api/profile/settings`: Updates user settings (e.g., profile picture).
|
|
* `GET /api/admin/dashboard`: Gets admin dashboard counts.
|
|
* `GET /api/admin/students`: Lists students (basic).
|
|
* `GET /api/admin/students/{student_id}/financials`: Gets (mocked) financial data for a student.
|
|
|
|
Refer to `src/handlers.rs` and `src/models.rs` for detailed request/response structures.
|
|
|
|
## Database
|
|
|
|
* **Type:** MariaDB (run via `mariadb:10.11` Docker image).
|
|
* **Interaction:** Asynchronous queries via `sqlx`.
|
|
* **Schema:** Defined and created in `src/db.rs::init_db`. Tables include `students`, `teachers`, `courses`, `enrollments`. SRP salt and verifier fields replace traditional password hashes.
|
|
* **Persistence:** Database data is persisted using a Docker named volume (`mariadb_data`) defined in `docker-compose.yml`.
|
|
* **Initial Data:** `src/db.rs::populate_initial_data` adds sample courses and users (students/teachers) **without any password/SRP data set**. Users cannot log in until a password-setting mechanism is implemented.
|
|
|
|
## Authentication (SRP)
|
|
|
|
* Uses the PAKE SRP-6a protocol.
|
|
* **Flow:**
|
|
1. Client sends `userId` to `/api/auth/srp/start`.
|
|
2. Server looks up user (student or teacher), finds SRP `salt` and `verifier` (if set), generates server ephemeral `B`, and stores temporary challenge state (**in memory**). Responds with `salt`, `B`, and `userType`.
|
|
3. Client calculates its ephemeral `A` and proof `M1`, sends `userId`, `userType`, `A`, `M1` to `/api/auth/srp/verify`.
|
|
4. Server retrieves challenge state, verifies `M1`. If valid, calculates server proof `M2`, generates a session token (UUID), stores session (**in memory**), and responds with `M2` and login data (including token).
|
|
5. Client verifies `M2`.
|
|
|
|
## Important Notes & TODOs
|
|
|
|
* 🚨 **Production State Management:** The current use of `AppState` (in-memory `Mutex<HashMap>`) for storing active SRP challenges and user sessions is **NOT suitable for production**. It will lose state on restart and doesn't scale. **Replace this** with a persistent solution like Redis or a dedicated database table for sessions and potentially challenges (with TTL).
|
|
* 🔐 **Authentication Middleware:** Protected routes (`/profile/*`, `/admin/*`, etc.) currently **lack proper token validation middleware**. Implement Actix middleware to verify the `Authorization: Bearer <token>` header against the active session store before allowing access.
|
|
* 🔑 **Password Setting/Registration:** There is currently **no mechanism** for users to set their initial password or change it. An endpoint needs to be created that takes a username/password, generates the SRP salt and verifier using `srp::server::generate_verifier`, and stores them in the database for the corresponding user.
|
|
* 🧪 **Testing:** Implement comprehensive unit and integration tests for handlers, database logic, and authentication flow.
|
|
* 📈 **Scalability:** Consider connection pool tuning (`max_connections` in `main.rs`) and potential optimizations for high-load scenarios.
|
|
* 📚 **API Documentation:** Consider generating API documentation (e.g., using OpenAPI specs).
|
|
|
|
## Contributing
|
|
|
|
<!-- Optional: Link to Contribution Guidelines if applicable -->
|
|
|
|
## License
|
|
|
|
<!-- Optional: Link to License file if applicable --> |