├── .dockerignore ├── .env.example ├── .github └── workflows │ ├── release.yml │ └── test.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── Dockerfile ├── LICENSE ├── README.md ├── docs ├── API.md └── CONFIGURATION.md └── src ├── components.rs ├── components ├── env_reader.rs └── open_api.rs ├── configuration.rs ├── configuration ├── config.rs ├── db_config.rs ├── default_user_config.rs ├── jwt_config.rs └── server_config.rs ├── errors.rs ├── errors ├── bad_request.rs └── internal_server_error.rs ├── lib.rs ├── main.rs ├── repository.rs ├── repository ├── audit.rs ├── audit │ ├── audit_model.rs │ └── audit_repository.rs ├── permission.rs ├── permission │ ├── permission_model.rs │ └── permission_repository.rs ├── role.rs ├── role │ ├── role_model.rs │ └── role_repository.rs ├── user.rs └── user │ ├── user_model.rs │ └── user_repository.rs ├── services.rs ├── services ├── audit.rs ├── audit │ └── audit_service.rs ├── jwt.rs ├── jwt │ └── jwt_service.rs ├── password.rs ├── password │ └── password_service.rs ├── permission.rs ├── permission │ └── permission_service.rs ├── role.rs ├── role │ └── role_service.rs ├── user.rs └── user │ └── user_service.rs ├── web.rs └── web ├── controller.rs ├── controller ├── audit.rs ├── audit │ └── audit_controller.rs ├── authentication.rs ├── authentication │ └── authentication_controller.rs ├── health.rs ├── health │ └── health_controller.rs ├── permission.rs ├── permission │ └── permission_controller.rs ├── role.rs ├── role │ └── role_controller.rs ├── user.rs └── user │ └── user_controller.rs ├── dto.rs ├── dto ├── audit.rs ├── audit │ └── audit_dto.rs ├── authentication.rs ├── authentication │ ├── login_request.rs │ ├── login_response.rs │ └── register_request.rs ├── permission.rs ├── permission │ ├── create_permission.rs │ ├── permission_dto.rs │ └── update_permission.rs ├── role.rs ├── role │ ├── create_role.rs │ ├── role_dto.rs │ └── update_role.rs ├── search.rs ├── search │ └── search_request.rs ├── user.rs └── user │ ├── create_user.rs │ ├── update_password.rs │ ├── update_user.rs │ └── user_dto.rs ├── extractors.rs └── extractors ├── jwt_extractor.rs └── user_id_extractor.rs /.dockerignore: -------------------------------------------------------------------------------- 1 | # Rust artifacts 2 | target/ 3 | .env.example 4 | .env 5 | README.md 6 | LICENSE 7 | 8 | # Editor and IDE specific files 9 | .vscode/ 10 | .idea/ 11 | .github/ 12 | docs/ 13 | 14 | # Version control 15 | .git/ 16 | .gitignore 17 | 18 | # Dependency sources (these can be fetched during the Docker build) 19 | /vendor/ 20 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # Server configuration 2 | SERVER_ADDR=0.0.0.0 3 | SERVER_PORT=8080 4 | SERVER_WORKERS=0 5 | MAX_FETCH_LIMIT=0 6 | 7 | # Database configuration 8 | DB_CONNECTION_STRING=mongodb+srv://:@cluster.mongodb.net/?retryWrites=true&w=majority 9 | DB_DATABASE=test 10 | DB_PERMISSION_COLLECTION=permissions 11 | DB_ROLE_COLLECTION=roles 12 | DB_USER_COLLECTION=users 13 | DB_AUDIT_COLLECTION=audits 14 | DB_CREATE_INDEXES=true 15 | DB_AUDIT_ENABLED=false 16 | DB_AUDIT_TTL=0 17 | 18 | # JWT 19 | JWT_SECRET=topSecretSecret 20 | JWT_EXPIRATION=3600 21 | 22 | # Logging 23 | RUST_LOG=info 24 | RUST_BACKTRACE=1 25 | 26 | # Default user 27 | GENERATE_DEFAULT_USER=true 28 | DEFAULT_USER_USERNAME=admin 29 | DEFAULT_USER_EMAIL=admin@opserva.io 30 | DEFAULT_USER_PASSWORD=123456 31 | DEFAULT_USER_ENABLED=true 32 | 33 | # OpenAPI 34 | ENABLE_OPENAPI=true 35 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | permissions: 9 | contents: write 10 | 11 | jobs: 12 | docker-publish: 13 | if: startsWith(github.ref, 'refs/tags/') 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v3 19 | 20 | - name: Set up Docker Buildx 21 | uses: docker/setup-buildx-action@v2 22 | 23 | - name: Login to Docker Hub 24 | uses: docker/login-action@v2 25 | with: 26 | username: ${{ secrets.DOCKERHUB_USERNAME }} 27 | password: ${{ secrets.DOCKERHUB_TOKEN }} 28 | 29 | - name: Publish 30 | uses: docker/build-push-action@v4 31 | with: 32 | context: . 33 | push: true 34 | tags: ${{ vars.DOCKERHUB_REPO }}:latest 35 | 36 | gh-release: 37 | if: startsWith(github.ref, 'refs/tags/') 38 | strategy: 39 | matrix: 40 | os: [ ubuntu-latest, macos-latest, windows-latest ] 41 | runs-on: ${{ matrix.os }} 42 | 43 | steps: 44 | - name: Checkout 45 | uses: actions/checkout@v3 46 | 47 | - name: Install latest Rust toolchain 48 | uses: dtolnay/rust-toolchain@stable 49 | 50 | - name: Rust cache 51 | uses: swatinem/rust-cache@v2 52 | 53 | - name: Build (release) 54 | run: cargo build --release 55 | 56 | - name: Rename (macOS) 57 | if: ${{ matrix.os == 'macos-latest' }} 58 | run: mv target/release/auth-rs target/release/auth-rs-macos 59 | 60 | - name: Rename (Linux) 61 | if: ${{ matrix.os == 'ubuntu-latest' }} 62 | run: mv target/release/auth-rs target/release/auth-rs-linux 63 | 64 | - name: Publish 65 | uses: softprops/action-gh-release@v1 66 | with: 67 | prerelease: false 68 | files: | 69 | target/release/auth-rs-linux 70 | target/release/auth-rs.exe 71 | target/release/auth-rs-macos 72 | env: 73 | GITHUB_TOKEN: ${{ github.token }} 74 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | permissions: read-all 4 | 5 | on: 6 | pull_request: 7 | branches: 8 | - master 9 | - development 10 | 11 | jobs: 12 | test: 13 | strategy: 14 | matrix: 15 | os: [ ubuntu-latest, macos-latest, windows-latest ] 16 | runs-on: ${{ matrix.os }} 17 | 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v3 21 | 22 | - name: Install latest Rust toolchain 23 | uses: dtolnay/rust-toolchain@stable 24 | 25 | - name: Rust cache 26 | uses: swatinem/rust-cache@v2 27 | 28 | - name: Check code formatting 29 | run: cargo fmt --all -- --check 30 | 31 | - name: Check code 32 | run: cargo check --workspace --verbose 33 | 34 | - name: Test code 35 | run: cargo test --verbose --all 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## File system 2 | .DS_Store 3 | desktop.ini 4 | 5 | ## Editor 6 | *.swp 7 | *.swo 8 | Session.vim 9 | .cproject 10 | .idea 11 | *.iml 12 | .vscode 13 | .project 14 | .favorites.json 15 | .settings/ 16 | .vs/ 17 | 18 | ## Tool 19 | .valgrindrc 20 | .cargo 21 | 22 | ## Build 23 | /target 24 | 25 | ## Env 26 | /.env 27 | .env 28 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "auth-rs" 3 | description = "Authorization and authentication service" 4 | version = "0.2.1" 5 | edition = "2021" 6 | rust-version = "1.74.1" 7 | authors = ["CodeDead "] 8 | readme = "README.md" 9 | license = "GPL-3.0-only" 10 | homepage = "https://codedead.com/" 11 | keywords = ["api", "authorization", "authentication", "auth"] 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | actix-web = "4" 17 | actix-cors = "0.6.5" 18 | actix-web-grants = "4.0.3" 19 | mongodb = { version = "2.7.1", features = ["bson-chrono-0_4"] } 20 | chrono = "0.4.31" 21 | serde = { version = "1.0", features = ["derive"] } 22 | serde_json = "1.0" 23 | futures = "0.3.29" 24 | dotenvy = "0.15.7" 25 | argon2 = "0.5.2" 26 | regex = "1.10.2" 27 | jsonwebtoken = "8.3.0" 28 | env_logger = "0.10.1" 29 | log = "0.4.20" 30 | utoipa = "4.1.0" 31 | utoipa-swagger-ui = { version = "5.0.0", features = ["actix-web"] } 32 | 33 | [profile.dev] 34 | panic = "abort" 35 | 36 | [profile.release] 37 | lto = true 38 | strip = "symbols" 39 | debug = false 40 | panic = "abort" 41 | opt-level = "z" 42 | codegen-units = 1 43 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the latest Rust image to build the application 2 | FROM rust:latest AS build 3 | 4 | # Set the working directory 5 | WORKDIR /usr/src/auth-rs 6 | 7 | # Copy the Cargo.toml and Cargo.lock files to the working directory 8 | COPY Cargo.toml Cargo.lock ./ 9 | 10 | # Copy the rest of the application code to the container 11 | COPY src ./src 12 | 13 | # Build the application 14 | RUN cargo build --release 15 | 16 | # Start a new stage for the runtime image 17 | FROM debian:stable-slim 18 | 19 | # Set the environment variables 20 | ENV SERVER_ADDR=0.0.0.0 21 | ENV SERVER_PORT=8080 22 | ENV DB_CONNECTION_STRING=mongodb+srv://:@cluster.mongodb.net/?retryWrites=true&w=majority 23 | ENV DB_DATABASE=test 24 | ENV DB_PERMISSION_COLLECTION=permissions 25 | ENV DB_ROLE_COLLECTION=roles 26 | ENV DB_USER_COLLECTION=users 27 | ENV DB_AUDIT_COLLECTION=audits 28 | ENV DB_CREATE_INDEXES=true 29 | ENV DB_AUDIT_ENABLED=false 30 | ENV JWT_SECRET=topSecretSecret 31 | ENV JWT_EXPIRATION=3600 32 | ENV RUST_LOG=info 33 | ENV RUST_BACKTRACE=1 34 | ENV GENERATE_DEFAULT_USER=true 35 | ENV DEFAULT_USER_USERNAME=admin 36 | ENV DEFAULT_USER_EMAIL=admin@opserva.io 37 | ENV DEFAULT_USER_PASSWORD=123456 38 | ENV DEFAULT_USER_ENABLED=true 39 | ENV MAX_FETCH_LIMIT=100 40 | ENV DB_AUDIT_TTL=0 41 | 42 | # Set the working directory 43 | WORKDIR /usr/src/auth-rs 44 | 45 | # Copy the binary from the build stage to the current stage 46 | COPY --from=build /usr/src/auth-rs/target/release/auth-rs . 47 | 48 | # Expose any ports your application might listen on (optional) 49 | EXPOSE 8080 50 | 51 | # Set the binary as the entrypoint of the container 52 | CMD ["./auth-rs"] 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # auth-rs 2 | 3 | ![GitHub top language](https://img.shields.io/github/languages/top/Opserva-io/auth-rs) 4 | ![GitHub](https://img.shields.io/github/license/Opserva-io/auth-rs) 5 | ![GitHub release (with filter)](https://img.shields.io/github/v/release/Opserva-io/auth-rs) 6 | [![Test](https://github.com/Opserva-io/auth-rs/actions/workflows/test.yml/badge.svg)](https://github.com/Opserva-io/auth-rs/actions/workflows/test.yml) 7 | 8 | `auth-rs` provides a simple authentication and authorization service for use in other services. 9 | The service is written in Rust and uses the [actix-web](https://crates.io/crates/actix-web) framework. 10 | 11 | Users, when authenticated, will be given a JWT token which can be used to access other services. 12 | 13 | A [React](https://react.dev/)-based frontend for `auth-rs` is also 14 | available [here](https://github.com/Opserva-io/auth-js). 15 | 16 | ## Table of Contents 17 | 18 | - [Features](#features) 19 | - [Usage](#usage) 20 | - [Development](#development) 21 | - [Docker](#docker) 22 | - [Configuration](#configuration) 23 | - [API](#api) 24 | - [Building](#building) 25 | - [Dependencies](#dependencies) 26 | - [About](#about) 27 | 28 | ## Features 29 | 30 | - Authentication 31 | - Authorization 32 | - Audit trail 33 | - Password hashing 34 | - JWT generation 35 | - JWT verification 36 | - Pagination 37 | - OpenAPI / Swagger UI 38 | - CORS support 39 | - Docker support 40 | - MongoDB integration 41 | - MongoDB Atlas integration 42 | 43 | ## Usage 44 | 45 | ### Development 46 | 47 | 1. Clone the repository 48 | 2. Copy `.env.example` to `.env` in the root of the project and fill in / adjust the values 49 | 3. Execute `cargo run` to start the service 50 | 51 | ### Docker 52 | 53 | You can execute `auth-rs` using Docker: 54 | 55 | ```bash 56 | docker run -d -p 8080:8080 --env-file .env opserva/auth-rs 57 | ``` 58 | 59 | ## Configuration 60 | 61 | `auth-rs` can be configured using environment variables to fit your environment and requirements. 62 | 63 | For more information about the environment variables, see the [Configuration documentation](/docs/CONFIGURATION.md). 64 | 65 | ## API 66 | 67 | `auth-rs` exposes a REST API that can be used to interact with the service using Create, Read, Update and Delete (CRUD) requests. 68 | Other (micro)services can use this API to authenticate and authorize users (and generate and verify JWT tokens). 69 | 70 | See the [full API documentation](/docs/API.md) for more information. 71 | 72 | ## Building 73 | 74 | In order to build `auth-rs`, you will need to have Rust installed. 75 | You can install Rust by following the instructions [here](https://www.rust-lang.org/tools/install). 76 | 77 | ### cargo 78 | 79 | You can build `auth-rs` using `cargo`: 80 | 81 | ```shell 82 | cargo build 83 | ``` 84 | 85 | You can build an optimized `release` version of `auth-rs` using `cargo`: 86 | 87 | ```shell 88 | cargo build --release 89 | ``` 90 | 91 | ### Docker 92 | 93 | You can build a docker image of `auth-rs` using the provided `Dockerfile`: 94 | 95 | ```shell 96 | docker build -t auth-rs . 97 | ``` 98 | 99 | ## Dependencies 100 | 101 | A couple of dependencies are required in order to build `auth-rs`: 102 | 103 | * [actix-web](https://crates.io/crates/actix-web) 104 | * [actix-cors](https://crates.io/crates/actix-cors) 105 | * [actix-web-grants](https://crates.io/crates/actix-web-grants) 106 | * [mongodb](https://crates.io/crates/mongodb) 107 | * [chrono](https://crates.io/crates/chrono) 108 | * [serde](https://crates.io/crates/serde) 109 | * [serde_json](https://crates.io/crates/serde_json) 110 | * [futures](https://crates.io/crates/futures) 111 | * [dotenvy](https://crates.io/crates/dotenvy) 112 | * [argon2](https://crates.io/crates/argon2) 113 | * [regex](https://crates.io/crates/regex) 114 | * [jsonwebtoken](https://crates.io/crates/jsonwebtoken) 115 | * [env_logger](https://crates.io/crates/env_logger) 116 | * [log](https://crates.io/crates/log) 117 | * [utoipa](https://crates.io/crates/utoipa) 118 | * [utoipa-swagger-ui](https://crates.io/crates/utoipa-swagger-ui) 119 | 120 | ## About 121 | 122 | This library is maintained by CodeDead. You can find more about us using the following links: 123 | 124 | * [Website](https://codedead.com) 125 | * [Twitter](https://twitter.com/C0DEDEAD) 126 | * [Facebook](https://facebook.com/deadlinecodedead) 127 | 128 | Copyright © 2023 [CodeDead](https://codedead.com) 129 | -------------------------------------------------------------------------------- /docs/CONFIGURATION.md: -------------------------------------------------------------------------------- 1 | # Configuration 2 | 3 | `auth-rs` can be configured using environment variables to fit your environment and requirements. 4 | 5 | ## Table of contents 6 | 7 | - [Environment variables](#environment-variables) 8 | - [Changing the default configuration](#changing-the-default-configuration) 9 | - [Docker](#docker) 10 | 11 | ## Environment variables 12 | 13 | The following environment variables can be used to configure `auth-rs`: 14 | 15 | | Variable | Default | Required | Type | Description | 16 | |--------------------------|---------------|----------------------------------------------|-------------|-------------------------------------------------------------------------| 17 | | SERVER_ADDR | `0.0.0.0` | `false` | `IPAddress` | The server address | 18 | | SERVER_PORT | `8080` | `false` | `u16` | The port that the server will use | 19 | | SERVER_WORKERS | `0` | `false` | `usize` | Sets number of workers to start (per bind address). | 20 | | MAX_FETCH_LIMIT | `100` | `false` | `i64` | The maximum amount of entity records that can be retrieved in one call | 21 | | DB_CONNECTION_STRING | N/A | `true` | `String` | The MongoDB connection string | 22 | | DB_DATABASE | N/A | `true` | `String` | The MongoDB Database that will be used by `auth-rs` | 23 | | DB_PERMISSION_COLLECTION | `permissions` | `false` | `String` | The collection that holds the `Permission` entities | 24 | | DB_ROLE_COLLECTION | `roles` | `false` | `String` | The collection that holds the `Role` entities | 25 | | DB_USER_COLLECTION | `users` | `false` | `String` | The collection that holds the `User` entities | 26 | | DB_AUDIT_COLLECTION | `audits` | `false` | `String` | The collection that holds the `Audit` entities | 27 | | DB_CREATE_INDEXES | `true` | `false` | `bool` | Automatically create collection indexes | 28 | | DB_AUDIT_ENABLED | `false` | `false` | `bool` | Enable or disable audit trails | 29 | | DB_AUDIT_TTL | `0` | `false` | `u64` | Automatically remove `Audit` entities after a set amount of seconds | 30 | | JWT_SECRET | N/A | `true` | `String` | The JWT secret | 31 | | JWT_EXPIRATION | `3600` | `false` | `usize` | The JWT expiration time in seconds | 32 | | RUST_LOG | N/A | `false` | `String` | The default log level | 33 | | RUST_BACKTRACE | N/A | `false` | `String` | Controls whether or not backtraces are displayed when a panic occurs | 34 | | GENERATE_DEFAULT_USER | `true` | `false` | `bool` | Sets whether a default administrator `User` should be generated | 35 | | DEFAULT_USER_USERNAME | N/A | `true` if `GENERATE_DEFAULT_USER` is enabled | `String` | The default `User`'s username | 36 | | DEFAULT_USER_EMAIL | N/A | `false` | `String` | The default `User`'s email address | 37 | | DEFAULT_USER_PASSWORD | N/A | `true` if `GENERATE_DEFAULT_USER` is enabled | `String` | The default `User`'s password | 38 | | DEFAULT_USER_ENABLED | N/A | `true` if `GENERATE_DEFAULT_USER` is enabled | `bool` | Sets whether the default user is enabled or not | 39 | | ENABLE_OPENAPI | `true` | `false` | `bool` | Enables or disables the OpenAPI endpoint | 40 | 41 | 42 | > *Note*: `SERVER_WORKERS` will use the number of logical cores available on the system, if set to zero. 43 | 44 | > *Note*: The audit trail feature is disabled by default and will have a noticeable performance impact when enabled. 45 | > Audit trails can be set to expire automatically after a set amount of seconds by changing the `DB_AUDIT_TTL` environment variable 46 | > to the desired amount of seconds (greater than zero), if the `DB_CREATE_INDEXES` variable is also enabled. 47 | 48 | ## Changing the default configuration 49 | 50 | The default configuration can be changed by setting the environment variables before starting the service. 51 | Alternatively, you can create a `.env` file in the root directory of the project and set the environment variables 52 | there. 53 | 54 | An example `.env` file can be found in the root of the repository, called `.env.example`. 55 | 56 | ## Docker 57 | 58 | The `auth-rs` service can be executed using Docker. The `Dockerfile` is located in the root of the repository. 59 | You can add environment variables when running the container using the `-e` flag. 60 | 61 | An example of running the container using Docker: 62 | 63 | ```bash 64 | docker run -d -p 8080:8080 -e DB_CONNECTION_STRING=mongodb://localhost:27017 -e DB_DATABASE=auth-rs -e -e JWT_SECRET=mysecret -e DEFAULT_USER_USERNAME=admin -e DEFAULT_USER_PASSWORD=secret -e DEFAULT_USER_ENABLED=true opserva/auth-rs 65 | ``` 66 | 67 | Alternatively, you can provide an `.env` file to the container using the `--env-file` flag: 68 | 69 | ```bash 70 | docker run -d -p 8080:8080 --env-file .env opserva/auth-rs 71 | ``` 72 | -------------------------------------------------------------------------------- /src/components.rs: -------------------------------------------------------------------------------- 1 | pub mod env_reader; 2 | pub mod open_api; 3 | -------------------------------------------------------------------------------- /src/components/env_reader.rs: -------------------------------------------------------------------------------- 1 | use crate::configuration::config::Config; 2 | use crate::configuration::db_config::DbConfig; 3 | use crate::configuration::default_user_config::DefaultUserConfig; 4 | use crate::configuration::jwt_config::JwtConfig; 5 | use crate::configuration::server_config::ServerConfig; 6 | use log::info; 7 | use std::env; 8 | 9 | pub struct EnvReader {} 10 | 11 | impl EnvReader { 12 | /// # Summary 13 | /// 14 | /// Reads the configuration from the environment variables. 15 | /// 16 | /// # Example 17 | /// 18 | /// ``` 19 | /// use crate::components::env_reader::EnvReader; 20 | /// use crate::configuration::config::Config; 21 | /// 22 | /// let config = EnvReader::read_configuration().await; 23 | /// ``` 24 | /// 25 | /// # Returns 26 | /// 27 | /// A Config instance. 28 | pub async fn read_configuration() -> Config { 29 | info!("Reading configuration from environment variables"); 30 | 31 | let addr = match env::var("SERVER_ADDR") { 32 | Ok(d) => d, 33 | Err(_) => String::from("0.0.0.0"), 34 | }; 35 | 36 | let port = match env::var("SERVER_PORT") { 37 | Ok(d) => { 38 | let res: u16 = d.trim().parse().expect("PORT must be a number"); 39 | res 40 | } 41 | Err(_) => 8080, 42 | }; 43 | 44 | let max_limit = match env::var("MAX_FETCH_LIMIT") { 45 | Ok(d) => { 46 | let res: i64 = d.trim().parse().expect("MAX_FETCH_LIMIT must be a number"); 47 | res 48 | } 49 | Err(_) => 100, 50 | }; 51 | 52 | let workers = match env::var("SERVER_WORKERS") { 53 | Ok(d) => { 54 | let res: usize = d 55 | .trim() 56 | .parse() 57 | .expect("SERVER_WORKERS must be a valid usize"); 58 | res 59 | } 60 | Err(_) => 0, 61 | }; 62 | 63 | let conn_string = match env::var("DB_CONNECTION_STRING") { 64 | Ok(d) => d, 65 | Err(_) => panic!("No connection string specified"), 66 | }; 67 | 68 | let database = match env::var("DB_DATABASE") { 69 | Ok(d) => d, 70 | Err(_) => panic!("No database specified"), 71 | }; 72 | 73 | let permission_collection = match env::var("DB_PERMISSION_COLLECTION") { 74 | Ok(d) => d, 75 | Err(_) => String::from("permissions"), 76 | }; 77 | 78 | let role_collection = match env::var("DB_ROLE_COLLECTION") { 79 | Ok(d) => d, 80 | Err(_) => String::from("roles"), 81 | }; 82 | 83 | let user_collection = match env::var("DB_USER_COLLECTION") { 84 | Ok(d) => d, 85 | Err(_) => String::from("users"), 86 | }; 87 | 88 | let audit_collection = match env::var("DB_AUDIT_COLLECTION") { 89 | Ok(d) => d, 90 | Err(_) => String::from("audits"), 91 | }; 92 | 93 | let jwt_secret = match env::var("JWT_SECRET") { 94 | Ok(d) => d, 95 | Err(_) => panic!("No JWT secret specified"), 96 | }; 97 | 98 | let jwt_expiration = match env::var("JWT_EXPIRATION") { 99 | Ok(d) => { 100 | let res: usize = d.trim().parse().expect("JWT_EXPIRATION must be a number"); 101 | res 102 | } 103 | Err(_) => 3600, 104 | }; 105 | 106 | let generate_default_user = match env::var("GENERATE_DEFAULT_USER") { 107 | Ok(d) => { 108 | let res: bool = d 109 | .trim() 110 | .parse() 111 | .expect("GENERATE_DEFAULT_USER must be a boolean"); 112 | res 113 | } 114 | Err(_) => true, 115 | }; 116 | 117 | let mut default_username = String::new(); 118 | let mut default_email = Some(String::new()); 119 | let mut default_password = String::new(); 120 | let mut default_user_enabled = false; 121 | 122 | if generate_default_user { 123 | default_username = match env::var("DEFAULT_USER_USERNAME") { 124 | Ok(d) => d, 125 | Err(_) => panic!("No default username specified"), 126 | }; 127 | 128 | default_email = match env::var("DEFAULT_USER_EMAIL") { 129 | Ok(d) => Some(d), 130 | Err(_) => None, 131 | }; 132 | 133 | default_password = match env::var("DEFAULT_USER_PASSWORD") { 134 | Ok(d) => d, 135 | Err(_) => panic!("No default password specified"), 136 | }; 137 | 138 | default_user_enabled = match env::var("DEFAULT_USER_ENABLED") { 139 | Ok(d) => { 140 | let res: bool = d 141 | .trim() 142 | .parse() 143 | .expect("DEFAULT_USER_ENABLED must be a boolean"); 144 | res 145 | } 146 | Err(_) => panic!("No default user enabled specified"), 147 | }; 148 | } 149 | 150 | let audit_enabled = match env::var("DB_AUDIT_ENABLED") { 151 | Ok(d) => { 152 | let res: bool = d 153 | .trim() 154 | .parse() 155 | .expect("DB_AUDIT_ENABLED must be a boolean"); 156 | res 157 | } 158 | Err(_) => false, 159 | }; 160 | 161 | let audit_ttl = match env::var("DB_AUDIT_TTL") { 162 | Ok(d) => { 163 | let res: u64 = d.trim().parse().expect("DB_AUDIT_TTL must be a number"); 164 | res 165 | } 166 | Err(_) => 0, 167 | }; 168 | 169 | let create_indexes = match env::var("DB_CREATE_INDEXES") { 170 | Ok(d) => { 171 | let res: bool = d 172 | .trim() 173 | .parse() 174 | .expect("DB_CREATE_INDEXES must be a boolean"); 175 | res 176 | } 177 | Err(_) => true, 178 | }; 179 | 180 | let enable_openapi = match env::var("ENABLE_OPENAPI") { 181 | Ok(d) => { 182 | let res: bool = d.trim().parse().expect("ENABLE_OPENAPI must be a boolean"); 183 | res 184 | } 185 | Err(_) => true, 186 | }; 187 | 188 | let default_user_config = DefaultUserConfig::new( 189 | default_username, 190 | default_email, 191 | default_password, 192 | default_user_enabled, 193 | ); 194 | 195 | let db_config = DbConfig::new( 196 | conn_string, 197 | database, 198 | permission_collection, 199 | role_collection, 200 | user_collection, 201 | audit_collection, 202 | create_indexes, 203 | audit_enabled, 204 | audit_ttl, 205 | ); 206 | 207 | let server_config = ServerConfig::new(addr, port, max_limit, workers); 208 | 209 | Config::new( 210 | server_config, 211 | db_config, 212 | default_user_config, 213 | generate_default_user, 214 | JwtConfig::new(jwt_secret, jwt_expiration), 215 | enable_openapi, 216 | ) 217 | .await 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /src/components/open_api.rs: -------------------------------------------------------------------------------- 1 | use utoipa::openapi::security::{Http, HttpAuthScheme, SecurityScheme}; 2 | use utoipa::OpenApi; 3 | use utoipa::{openapi, Modify}; 4 | 5 | pub struct SecurityAddon; 6 | impl Modify for SecurityAddon { 7 | /// # Summary 8 | /// 9 | /// Adds the security scheme to the OpenAPI specification. 10 | /// 11 | /// # Arguments 12 | /// 13 | /// * `openapi` - A mutable reference to the OpenAPI specification. 14 | /// 15 | fn modify(&self, openapi: &mut openapi::OpenApi) { 16 | let components = openapi.components.as_mut().unwrap(); 17 | components.add_security_scheme( 18 | "Token", 19 | SecurityScheme::Http(Http::new(HttpAuthScheme::Bearer)), 20 | ); 21 | } 22 | } 23 | 24 | #[derive(OpenApi)] 25 | #[openapi( 26 | paths( 27 | crate::web::controller::authentication::authentication_controller::login, 28 | crate::web::controller::authentication::authentication_controller::register, 29 | crate::web::controller::authentication::authentication_controller::current_user, 30 | crate::web::controller::health::health_controller::health, 31 | crate::web::controller::permission::permission_controller::create_permission, 32 | crate::web::controller::permission::permission_controller::find_all_permissions, 33 | crate::web::controller::permission::permission_controller::find_by_id, 34 | crate::web::controller::permission::permission_controller::update_permission, 35 | crate::web::controller::permission::permission_controller::delete_permission, 36 | crate::web::controller::role::role_controller::create, 37 | crate::web::controller::role::role_controller::find_all_roles, 38 | crate::web::controller::role::role_controller::find_by_id, 39 | crate::web::controller::role::role_controller::update, 40 | crate::web::controller::role::role_controller::delete, 41 | crate::web::controller::user::user_controller::create, 42 | crate::web::controller::user::user_controller::find_all, 43 | crate::web::controller::user::user_controller::find_by_id, 44 | crate::web::controller::user::user_controller::update, 45 | crate::web::controller::user::user_controller::update_self, 46 | crate::web::controller::user::user_controller::update_password, 47 | crate::web::controller::user::user_controller::admin_update_password, 48 | crate::web::controller::user::user_controller::delete, 49 | crate::web::controller::user::user_controller::delete_self, 50 | crate::web::controller::audit::audit_controller::find_all, 51 | crate::web::controller::audit::audit_controller::find_by_id, 52 | ), 53 | components( 54 | schemas( 55 | crate::errors::internal_server_error::InternalServerError, 56 | crate::errors::bad_request::BadRequest, 57 | crate::web::dto::permission::create_permission::CreatePermission, 58 | crate::web::dto::permission::permission_dto::PermissionDto, 59 | crate::web::dto::permission::update_permission::UpdatePermission, 60 | crate::web::controller::health::health_controller::HealthResponse, 61 | crate::web::dto::authentication::login_request::LoginRequest, 62 | crate::web::dto::authentication::login_response::LoginResponse, 63 | crate::web::dto::authentication::register_request::RegisterRequest, 64 | crate::web::dto::user::user_dto::SimpleUserDto, 65 | crate::web::dto::role::role_dto::SimpleRoleDto, 66 | crate::web::dto::permission::permission_dto::SimplePermissionDto, 67 | crate::web::dto::role::role_dto::RoleDto, 68 | crate::web::dto::role::create_role::CreateRole, 69 | crate::web::dto::role::update_role::UpdateRole, 70 | crate::web::dto::user::create_user::CreateUser, 71 | crate::web::dto::user::user_dto::UserDto, 72 | crate::web::dto::user::update_user::UpdateUser, 73 | crate::web::dto::user::update_user::UpdateOwnUser, 74 | crate::web::dto::user::update_password::UpdatePassword, 75 | crate::web::dto::user::update_password::AdminUpdatePassword, 76 | crate::web::dto::audit::audit_dto::AuditDto, 77 | crate::web::dto::audit::audit_dto::ActionDto, 78 | crate::web::dto::audit::audit_dto::ResourceIdTypeDto, 79 | crate::web::dto::audit::audit_dto::ResourceTypeDto, 80 | ) 81 | ), 82 | modifiers(&SecurityAddon) 83 | )] 84 | pub struct ApiDoc; 85 | -------------------------------------------------------------------------------- /src/configuration.rs: -------------------------------------------------------------------------------- 1 | pub mod config; 2 | pub mod db_config; 3 | pub mod default_user_config; 4 | pub mod jwt_config; 5 | pub mod server_config; 6 | -------------------------------------------------------------------------------- /src/configuration/db_config.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Serialize, Deserialize)] 4 | pub struct DbConfig { 5 | pub connection_string: String, 6 | pub database_name: String, 7 | pub permission_collection: String, 8 | pub role_collection: String, 9 | pub user_collection: String, 10 | pub audit_collection: String, 11 | pub create_indexes: bool, 12 | pub audit_enabled: bool, 13 | pub audit_ttl: u64, 14 | } 15 | 16 | impl DbConfig { 17 | /// # Summary 18 | /// 19 | /// Creates a new DbConfig instance. 20 | /// 21 | /// # Arguments 22 | /// 23 | /// * `connection_string` - A String that holds the connection string. 24 | /// * `database_name` - A String that holds the database name. 25 | /// * `permission_collection` - A String that holds the permission collection name. 26 | /// * `role_collection` - A String that holds the role collection name. 27 | /// * `user_collection` - A String that holds the user collection name. 28 | /// * `audit_collection` - A String that holds the audit collection name. 29 | /// * `create_indexes` - A bool that indicates whether to create indexes or not. 30 | /// * `audit_enabled` - A bool that indicates whether auditing is enabled or not. 31 | /// * `audit_ttl` - A u64 that holds the audit TTL. 32 | /// 33 | /// # Returns 34 | /// 35 | /// A DbConfig instance. 36 | #[allow(clippy::too_many_arguments)] 37 | pub fn new( 38 | connection_string: String, 39 | database_name: String, 40 | permission_collection: String, 41 | role_collection: String, 42 | user_collection: String, 43 | audit_collection: String, 44 | create_indexes: bool, 45 | audit_enabled: bool, 46 | audit_ttl: u64, 47 | ) -> DbConfig { 48 | DbConfig { 49 | connection_string, 50 | database_name, 51 | permission_collection, 52 | role_collection, 53 | user_collection, 54 | audit_collection, 55 | create_indexes, 56 | audit_enabled, 57 | audit_ttl, 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/configuration/default_user_config.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone)] 2 | pub struct DefaultUserConfig { 3 | pub username: String, 4 | pub email: Option, 5 | pub password: String, 6 | pub enabled: bool, 7 | } 8 | 9 | impl DefaultUserConfig { 10 | /// # Summary 11 | /// 12 | /// Creates a new DefaultUserConfig instance. 13 | /// 14 | /// # Arguments 15 | /// 16 | /// * `username` - A String that holds the default username. 17 | /// * `email` - A String that holds the default email. 18 | /// * `password` - A String that holds the default password. 19 | /// * `enabled` - A bool that holds the default enabled value. 20 | /// 21 | /// # Returns 22 | /// 23 | /// A DefaultUserConfig instance. 24 | pub fn new( 25 | username: String, 26 | email: Option, 27 | password: String, 28 | enabled: bool, 29 | ) -> DefaultUserConfig { 30 | DefaultUserConfig { 31 | username, 32 | email, 33 | password, 34 | enabled, 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/configuration/jwt_config.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone)] 2 | pub struct JwtConfig { 3 | pub jwt_secret: String, 4 | pub jwt_expiration: usize, 5 | } 6 | 7 | impl JwtConfig { 8 | /// # Summary 9 | /// 10 | /// Create a new JwtConfig. 11 | /// 12 | /// # Arguments 13 | /// 14 | /// * `jwt_secret` - The secret to use for signing and verifying JWTs. 15 | /// * `jwt_expiration` - The expiration time in seconds. 16 | /// 17 | /// # Example 18 | /// 19 | /// ``` 20 | /// let jwt_config = JwtConfig::new(String::from("secret"), 3600); 21 | /// ``` 22 | /// 23 | /// # Returns 24 | /// 25 | /// * `JwtConfig` - The new JwtConfig. 26 | pub fn new(jwt_secret: String, jwt_expiration: usize) -> JwtConfig { 27 | JwtConfig { 28 | jwt_secret, 29 | jwt_expiration, 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/configuration/server_config.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone)] 2 | pub struct ServerConfig { 3 | pub address: String, 4 | pub port: u16, 5 | pub max_limit: i64, 6 | pub workers: usize, 7 | } 8 | 9 | impl ServerConfig { 10 | /// # Summary 11 | /// 12 | /// Create a new ServerConfig. 13 | /// 14 | /// # Arguments 15 | /// 16 | /// * `address` - The address of the ServerConfig. 17 | /// * `port` - The port of the ServerConfig. 18 | /// * `max_limit` - The maximum amount of entity records that can be retrieved in one call. 19 | /// * `workers` - The number of workers to start (per bind address). 20 | /// 21 | /// # Example 22 | /// 23 | /// ``` 24 | /// let server_config = ServerConfig::new(String::from("address"), 8080); 25 | /// ``` 26 | /// 27 | /// # Returns 28 | /// 29 | /// * `ServerConfig` - The new ServerConfig. 30 | pub fn new(address: String, port: u16, max_limit: i64, workers: usize) -> ServerConfig { 31 | ServerConfig { 32 | address, 33 | port, 34 | max_limit, 35 | workers, 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/errors.rs: -------------------------------------------------------------------------------- 1 | pub mod bad_request; 2 | pub mod internal_server_error; 3 | -------------------------------------------------------------------------------- /src/errors/bad_request.rs: -------------------------------------------------------------------------------- 1 | use chrono::{DateTime, Utc}; 2 | use serde::Serialize; 3 | use std::time::SystemTime; 4 | use utoipa::ToSchema; 5 | 6 | #[derive(Serialize, ToSchema)] 7 | pub struct BadRequest { 8 | message: String, 9 | timestamp: String, 10 | #[serde(rename(serialize = "errorCode", deserialize = "errorCode"))] 11 | error_code: u16, 12 | } 13 | 14 | impl BadRequest { 15 | /// # Summary 16 | /// 17 | /// Create a new BadRequest. 18 | /// 19 | /// # Arguments 20 | /// 21 | /// * `message` - The error message. 22 | /// 23 | /// # Example 24 | /// 25 | /// ``` 26 | /// let bad_request = BadRequest::new("Bad Request"); 27 | /// ``` 28 | /// # Returns 29 | /// 30 | /// * `BadRequest` - The new BadRequest. 31 | /// 32 | pub fn new(message: &str) -> BadRequest { 33 | let now: DateTime = SystemTime::now().into(); 34 | let now: String = now.to_rfc3339(); 35 | 36 | BadRequest { 37 | message: String::from(message), 38 | timestamp: now, 39 | error_code: 400, 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/errors/internal_server_error.rs: -------------------------------------------------------------------------------- 1 | use chrono::{DateTime, Utc}; 2 | use serde::Serialize; 3 | use std::time::SystemTime; 4 | use utoipa::ToSchema; 5 | 6 | #[derive(Serialize, ToSchema)] 7 | pub struct InternalServerError { 8 | message: String, 9 | timestamp: String, 10 | #[serde(rename(serialize = "errorCode", deserialize = "errorCode"))] 11 | error_code: u16, 12 | } 13 | 14 | impl InternalServerError { 15 | /// # Summary 16 | /// 17 | /// Create a new InternalServerError. 18 | /// 19 | /// # Arguments 20 | /// 21 | /// * `message` - The error message. 22 | /// 23 | /// # Example 24 | /// 25 | /// ``` 26 | /// let internal_server_error = InternalServerError::new("Internal Server Error"); 27 | /// ``` 28 | /// # Returns 29 | /// 30 | /// * `InternalServerError` - The new InternalServerError. 31 | /// 32 | pub fn new(message: &str) -> InternalServerError { 33 | let now: DateTime = SystemTime::now().into(); 34 | let now: String = now.to_rfc3339(); 35 | 36 | InternalServerError { 37 | message: String::from(message), 38 | timestamp: now, 39 | error_code: 500, 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Summary 2 | //! The main module of the application 3 | //! 4 | //! # Description 5 | //! 6 | //! The main module of the application. It contains the main function which is the entry point to the application. 7 | //! 8 | //! # Remarks 9 | //! 10 | //! The application is built using the Actix Web framework. The application is configured using the .env file. 11 | //! 12 | //! # Maintainers 13 | //! 14 | //! * [CodeDead](https://codedead.com) 15 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use crate::components::env_reader::EnvReader; 2 | use crate::components::open_api::ApiDoc; 3 | use crate::web::controller::Controller; 4 | use actix_cors::Cors; 5 | use actix_web::middleware::Logger; 6 | use actix_web::{web as a_web, App, HttpServer}; 7 | use actix_web_grants::GrantsMiddleware; 8 | use dotenvy::dotenv; 9 | use env_logger::Env; 10 | use log::info; 11 | use utoipa::OpenApi; 12 | use utoipa_swagger_ui::SwaggerUi; 13 | 14 | mod components; 15 | mod configuration; 16 | mod errors; 17 | mod repository; 18 | mod services; 19 | mod web; 20 | 21 | /// # Summary 22 | /// 23 | /// The entry point to the application 24 | /// 25 | /// # Description 26 | /// 27 | /// The main function is the entry point to the application. It reads the configuration from the .env file 28 | /// and starts the application server based on the configuration. 29 | /// 30 | /// # Returns 31 | /// 32 | /// Returns a Result of type std::io::Result<()>. If the server starts successfully, it returns Ok(()). 33 | /// Otherwise, it returns an Err with an error message. 34 | #[actix_web::main] 35 | async fn main() -> std::io::Result<()> { 36 | dotenv().ok(); 37 | env_logger::init_from_env(Env::default().default_filter_or("info")); 38 | 39 | let config = EnvReader::read_configuration().await; 40 | 41 | let addr = config.server_config.address.clone(); 42 | let port = config.server_config.port; 43 | let workers = config.server_config.workers; 44 | 45 | info!("Starting server at {}:{}", addr, port); 46 | 47 | let openapi = ApiDoc::openapi(); 48 | 49 | let mut server = HttpServer::new(move || { 50 | let logger = Logger::default(); 51 | let mut app = App::new() 52 | .wrap(logger) 53 | .wrap(GrantsMiddleware::with_extractor( 54 | web::extractors::jwt_extractor::extract, 55 | )) 56 | .app_data(a_web::Data::new(config.clone())) 57 | .wrap(Cors::permissive()) 58 | .configure(Controller::configure_routes); 59 | 60 | if config.open_api { 61 | app = app.service( 62 | SwaggerUi::new("/swagger-ui/{_:.*}").url("/api-docs/openapi.json", openapi.clone()), 63 | ); 64 | } 65 | 66 | app 67 | }) 68 | .bind((addr, port)) 69 | .expect("Failed to bind server"); 70 | 71 | if workers > 0 { 72 | server = server.workers(workers); 73 | } 74 | 75 | server.run().await 76 | } 77 | -------------------------------------------------------------------------------- /src/repository.rs: -------------------------------------------------------------------------------- 1 | pub mod audit; 2 | pub mod permission; 3 | pub mod role; 4 | pub mod user; 5 | -------------------------------------------------------------------------------- /src/repository/audit.rs: -------------------------------------------------------------------------------- 1 | pub mod audit_model; 2 | pub mod audit_repository; 3 | -------------------------------------------------------------------------------- /src/repository/audit/audit_model.rs: -------------------------------------------------------------------------------- 1 | use chrono::{DateTime, Utc}; 2 | use mongodb::bson::oid::ObjectId; 3 | use serde::{Deserialize, Serialize}; 4 | use std::fmt::{Display, Formatter}; 5 | use std::time::SystemTime; 6 | 7 | #[derive(Serialize, Deserialize)] 8 | pub enum ResourceType { 9 | #[serde(rename = "permission")] 10 | Permission, 11 | #[serde(rename = "role")] 12 | Role, 13 | #[serde(rename = "user")] 14 | User, 15 | } 16 | 17 | impl Display for ResourceType { 18 | /// # Summary 19 | /// 20 | /// Display the ResourceType. 21 | /// 22 | /// # Arguments 23 | /// 24 | /// * `f` - A mutable reference to a Formatter. 25 | /// 26 | /// # Returns 27 | /// 28 | /// A std::fmt::Result. 29 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 30 | match self { 31 | ResourceType::Permission => write!(f, "Permission"), 32 | ResourceType::Role => write!(f, "Role"), 33 | ResourceType::User => write!(f, "User"), 34 | } 35 | } 36 | } 37 | 38 | #[derive(Serialize, Deserialize)] 39 | pub enum ResourceIdType { 40 | #[serde(rename = "permissionId")] 41 | PermissionId, 42 | #[serde(rename = "permissionIdVec")] 43 | PermissionIdVec, 44 | #[serde(rename = "permissionName")] 45 | PermissionName, 46 | #[serde(rename = "permissionSearch")] 47 | PermissionSearch, 48 | #[serde(rename = "roleId")] 49 | RoleId, 50 | #[serde(rename = "roleIdVec")] 51 | RoleIdVec, 52 | #[serde(rename = "roleName")] 53 | RoleName, 54 | #[serde(rename = "roleSearch")] 55 | RoleSearch, 56 | #[serde(rename = "userId")] 57 | UserId, 58 | #[serde(rename = "userName")] 59 | UserName, 60 | #[serde(rename = "userSearch")] 61 | UserSearch, 62 | #[serde(rename = "none")] 63 | None, 64 | } 65 | 66 | impl Display for ResourceIdType { 67 | /// # Summary 68 | /// 69 | /// Display the ResourceIdType. 70 | /// 71 | /// # Arguments 72 | /// 73 | /// * `f` - A mutable reference to a Formatter. 74 | /// 75 | /// # Returns 76 | /// 77 | /// A std::fmt::Result. 78 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 79 | match self { 80 | ResourceIdType::PermissionId => write!(f, "PermissionId"), 81 | ResourceIdType::PermissionIdVec => write!(f, "PermissionIdVec"), 82 | ResourceIdType::PermissionName => write!(f, "PermissionName"), 83 | ResourceIdType::PermissionSearch => write!(f, "PermissionSearch"), 84 | ResourceIdType::RoleId => write!(f, "RoleId"), 85 | ResourceIdType::RoleIdVec => write!(f, "RoleIdVec"), 86 | ResourceIdType::RoleName => write!(f, "RoleName"), 87 | ResourceIdType::RoleSearch => write!(f, "RoleSearch"), 88 | ResourceIdType::UserId => write!(f, "UserId"), 89 | ResourceIdType::UserName => write!(f, "UserName"), 90 | ResourceIdType::UserSearch => write!(f, "UserSearch"), 91 | ResourceIdType::None => write!(f, "None"), 92 | } 93 | } 94 | } 95 | 96 | #[derive(Serialize, Deserialize)] 97 | pub enum Action { 98 | #[serde(rename = "create")] 99 | Create, 100 | #[serde(rename = "update")] 101 | Update, 102 | #[serde(rename = "delete")] 103 | Delete, 104 | } 105 | 106 | impl Display for Action { 107 | /// # Summary 108 | /// 109 | /// Display the Action. 110 | /// 111 | /// # Arguments 112 | /// 113 | /// * `f` - A mutable reference to a Formatter. 114 | /// 115 | /// # Returns 116 | /// 117 | /// A std::fmt::Result. 118 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 119 | match self { 120 | Action::Create => write!(f, "Create"), 121 | Action::Update => write!(f, "Update"), 122 | Action::Delete => write!(f, "Delete"), 123 | } 124 | } 125 | } 126 | 127 | #[derive(Serialize, Deserialize)] 128 | pub struct Audit { 129 | #[serde(rename = "_id")] 130 | pub id: ObjectId, 131 | #[serde(rename = "userId")] 132 | pub user_id: ObjectId, 133 | pub action: Action, 134 | #[serde(rename = "resourceId")] 135 | pub resource_id: ObjectId, 136 | #[serde(rename = "resourceIdType")] 137 | pub resource_id_type: ResourceIdType, 138 | #[serde(rename = "resourceType")] 139 | pub resource_type: ResourceType, 140 | #[serde(with = "mongodb::bson::serde_helpers::chrono_datetime_as_bson_datetime")] 141 | #[serde(rename = "createdAt")] 142 | pub created_at: DateTime, 143 | } 144 | 145 | impl Audit { 146 | /// # Summary 147 | /// 148 | /// Create a new Audit. 149 | /// 150 | /// # Arguments 151 | /// 152 | /// * `user_id` - The user id of the Audit. 153 | /// * `action` - The action of the Audit. 154 | /// * `resource_id` - The resource id of the Audit. 155 | /// * `resource_id_type` - The resource id type of the Audit. 156 | /// * `resource_type` - The resource type of the Audit. 157 | /// 158 | /// # Example 159 | /// 160 | /// ``` 161 | /// use crate::repository::audit::audit_model::{Action, Audit, ResourceIdType, ResourceType}; 162 | /// ``` 163 | /// 164 | /// # Returns 165 | /// 166 | /// * `Audit` - The new Audit. 167 | pub fn new( 168 | user_id: ObjectId, 169 | action: Action, 170 | resource_id: ObjectId, 171 | resource_id_type: ResourceIdType, 172 | resource_type: ResourceType, 173 | ) -> Audit { 174 | let now: DateTime = SystemTime::now().into(); 175 | 176 | Audit { 177 | id: ObjectId::new(), 178 | user_id, 179 | action, 180 | resource_id, 181 | resource_id_type, 182 | resource_type, 183 | created_at: now, 184 | } 185 | } 186 | } 187 | 188 | impl Display for Audit { 189 | /// # Summary 190 | /// 191 | /// Display the Audit. 192 | /// 193 | /// # Arguments 194 | /// 195 | /// * `f` - The Formatter. 196 | /// 197 | /// # Returns 198 | /// 199 | /// A std::fmt::Result. 200 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 201 | write!( 202 | f, 203 | "Audit {{ id: {}, user_id: {}, action: {}, resource_id: {}, resource_type: {}, created_at: {} }}", 204 | self.id, self.user_id.to_hex(), self.action, self.resource_id.to_hex(), self.resource_type, self.created_at 205 | ) 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/repository/audit/audit_repository.rs: -------------------------------------------------------------------------------- 1 | use crate::repository::audit::audit_model::Audit; 2 | use futures::TryStreamExt; 3 | use mongodb::bson::doc; 4 | use mongodb::bson::oid::ObjectId; 5 | use mongodb::error::Error as MongodbError; 6 | use mongodb::options::FindOptions; 7 | use mongodb::Database; 8 | use std::fmt::{Display, Formatter}; 9 | 10 | #[derive(Clone)] 11 | pub struct AuditRepository { 12 | pub collection: String, 13 | } 14 | 15 | #[derive(Debug, Clone)] 16 | pub enum Error { 17 | InvalidId(String), 18 | EmptyCollection, 19 | EmptyTextSearch, 20 | MongoDb(MongodbError), 21 | ObjectId(String), 22 | } 23 | 24 | impl Display for Error { 25 | /// # Summary 26 | /// 27 | /// Display the Error. 28 | /// 29 | /// # Arguments 30 | /// 31 | /// * `f` - A mutable reference to a Formatter. 32 | /// 33 | /// # Returns 34 | /// 35 | /// A std::fmt::Result. 36 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 37 | match self { 38 | Error::InvalidId(id) => write!(f, "Invalid Audit ID: {}", id), 39 | Error::EmptyCollection => write!(f, "Empty Audit collection"), 40 | Error::EmptyTextSearch => write!(f, "Empty text search"), 41 | Error::MongoDb(e) => write!(f, "MongoDb Error: {}", e), 42 | Error::ObjectId(e) => write!(f, "ObjectId Error: {}", e), 43 | } 44 | } 45 | } 46 | 47 | impl AuditRepository { 48 | /// # Summary 49 | /// 50 | /// Create a new AuditRepository. 51 | /// 52 | /// # Arguments 53 | /// 54 | /// * `collection` - The collection name. 55 | /// 56 | /// # Returns 57 | /// 58 | /// * `Result` - The result of the operation. 59 | pub fn new(collection: String) -> Result { 60 | if collection.is_empty() { 61 | return Err(Error::EmptyCollection); 62 | } 63 | 64 | Ok(AuditRepository { collection }) 65 | } 66 | 67 | /// # Summary 68 | /// 69 | /// Create a new Audit. 70 | /// 71 | /// # Arguments 72 | /// 73 | /// * `audit` - The Audit to create. 74 | /// * `db` - The Database to create the Audit in. 75 | /// 76 | /// # Returns 77 | /// 78 | /// * `Result<(), Error>` - The result of the operation. 79 | pub async fn create(&self, audit: Audit, db: &Database) -> Result<(), Error> { 80 | match db 81 | .collection::(&self.collection) 82 | .insert_one(audit, None) 83 | .await 84 | { 85 | Ok(_) => Ok(()), 86 | Err(e) => Err(Error::MongoDb(e)), 87 | } 88 | } 89 | 90 | /// # Summary 91 | /// 92 | /// Find an Audit by id. 93 | /// 94 | /// # Arguments 95 | /// 96 | /// * `id` - The id of the Audit to find. 97 | /// * `db` - The Database to find the Audit in. 98 | /// 99 | /// # Returns 100 | /// 101 | /// * `Result, Error>` - The result of the operation. 102 | pub async fn find_by_id(&self, id: &str, db: &Database) -> Result, Error> { 103 | let target_object_id = match ObjectId::parse_str(id) { 104 | Ok(res) => res, 105 | Err(e) => { 106 | return Err(Error::InvalidId(e.to_string())); 107 | } 108 | }; 109 | 110 | match db 111 | .collection::(&self.collection) 112 | .find_one(doc! {"_id": target_object_id}, None) 113 | .await 114 | { 115 | Ok(r) => Ok(r), 116 | Err(e) => Err(Error::MongoDb(e)), 117 | } 118 | } 119 | 120 | /// # Summary 121 | /// 122 | /// Find all Audits. 123 | /// 124 | /// # Arguments 125 | /// 126 | /// * `limit` - The limit of Audits to find. 127 | /// * `page` - The page of Audits to find. 128 | /// * `db` - The Database to find the Audits in. 129 | /// 130 | /// # Returns 131 | /// 132 | /// * `Result, Error>` - The result of the operation. 133 | pub async fn find_all( 134 | &self, 135 | limit: Option, 136 | page: Option, 137 | db: &Database, 138 | ) -> Result, Error> { 139 | let mut skip: Option = None; 140 | 141 | if let Some(l) = limit { 142 | if l > 1 { 143 | if let Some(p) = page { 144 | if p > 1 { 145 | let res = u64::try_from((p - 1) * l).unwrap_or(0); 146 | skip = Some(res); 147 | } 148 | } 149 | } 150 | } 151 | 152 | let find_options = FindOptions::builder().limit(limit).skip(skip).build(); 153 | 154 | match db 155 | .collection::(&self.collection) 156 | .find(None, find_options) 157 | .await 158 | { 159 | Ok(r) => Ok(r.try_collect().await.unwrap_or_else(|_| vec![])), 160 | Err(e) => Err(Error::MongoDb(e)), 161 | } 162 | } 163 | 164 | /// # Summary 165 | /// 166 | /// Search for Audit entities. 167 | /// 168 | /// # Arguments 169 | /// 170 | /// * `text` - The text to search for. 171 | /// * `page` - The page of Audits to find. 172 | /// * `limit` - The limit of Audits to find. 173 | /// * `db` - The database to use. 174 | /// 175 | /// # Example 176 | /// 177 | /// ``` 178 | /// let audit_repository = AuditRepository::new("audit".to_string()).unwrap(); 179 | /// let db = mongodb::Client::with_uri_str("mongodb://localhost:27017") 180 | /// .await 181 | /// .unwrap() 182 | /// .database("test"); 183 | /// 184 | /// let result = audit_repository.search("", Some(100), Some(1), &db).await; 185 | /// ``` 186 | /// 187 | /// # Returns 188 | /// 189 | /// * `Result, Error>` - The result of the operation. 190 | pub async fn search( 191 | &self, 192 | text: &str, 193 | limit: Option, 194 | page: Option, 195 | db: &Database, 196 | ) -> Result, Error> { 197 | if text.is_empty() { 198 | return Err(Error::EmptyTextSearch); 199 | } 200 | 201 | let mut skip: Option = None; 202 | 203 | if let Some(l) = limit { 204 | if l > 1 { 205 | if let Some(p) = page { 206 | if p > 1 { 207 | let res = u64::try_from((p - 1) * l).unwrap_or(0); 208 | skip = Some(res); 209 | } 210 | } 211 | } 212 | } 213 | 214 | let find_options = FindOptions::builder().limit(limit).skip(skip).build(); 215 | 216 | let filter = doc! { 217 | "$text": { 218 | "$search": text, 219 | }, 220 | }; 221 | 222 | let cursor = match db 223 | .collection::(&self.collection) 224 | .find(filter, find_options) 225 | .await 226 | { 227 | Ok(d) => d, 228 | Err(e) => return Err(Error::MongoDb(e)), 229 | }; 230 | 231 | Ok(cursor.try_collect().await.unwrap_or_else(|_| vec![])) 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /src/repository/permission.rs: -------------------------------------------------------------------------------- 1 | pub mod permission_model; 2 | pub mod permission_repository; 3 | -------------------------------------------------------------------------------- /src/repository/permission/permission_model.rs: -------------------------------------------------------------------------------- 1 | use crate::web::dto::permission::create_permission::CreatePermission; 2 | use chrono::{DateTime, Utc}; 3 | use mongodb::bson::oid::ObjectId; 4 | use serde::{Deserialize, Serialize}; 5 | use std::fmt::{Display, Formatter}; 6 | use std::time::SystemTime; 7 | 8 | #[derive(Serialize, Deserialize, Clone)] 9 | pub struct Permission { 10 | #[serde(rename = "_id")] 11 | pub id: ObjectId, 12 | pub name: String, 13 | pub description: Option, 14 | #[serde(with = "mongodb::bson::serde_helpers::chrono_datetime_as_bson_datetime")] 15 | #[serde(rename = "createdAt")] 16 | pub created_at: DateTime, 17 | #[serde(with = "mongodb::bson::serde_helpers::chrono_datetime_as_bson_datetime")] 18 | #[serde(rename = "updatedAt")] 19 | pub updated_at: DateTime, 20 | } 21 | 22 | impl Permission { 23 | /// # Summary 24 | /// 25 | /// Create a new Permission. 26 | /// 27 | /// # Arguments 28 | /// 29 | /// * `name` - The name of the Permission. 30 | /// * `description` - The description of the Permission. 31 | /// 32 | /// # Example 33 | /// 34 | /// ``` 35 | /// let permission = Permission::new(String::from("Permission Name"), Some(String::from("Permission Description"))); 36 | /// ``` 37 | /// 38 | /// # Returns 39 | /// 40 | /// * `Permission` - The new Permission. 41 | pub fn new(name: String, description: Option) -> Self { 42 | let now: DateTime = SystemTime::now().into(); 43 | 44 | Permission { 45 | id: ObjectId::new(), 46 | name, 47 | description, 48 | created_at: now, 49 | updated_at: now, 50 | } 51 | } 52 | } 53 | 54 | impl From for Permission { 55 | /// # Summary 56 | /// 57 | /// Convert a CreatePermission into a Permission. 58 | /// 59 | /// # Arguments 60 | /// 61 | /// * `permission` - The CreatePermission to convert. 62 | /// 63 | /// # Example 64 | /// 65 | /// ``` 66 | /// let create_permission = CreatePermission { 67 | /// name: String::from("Permission Name"), 68 | /// description: Some(String::from("Permission Description")), 69 | /// }; 70 | /// 71 | /// let permission = Permission::from(create_permission); 72 | /// ``` 73 | /// # Returns 74 | /// 75 | /// * `Permission` - The new Permission. 76 | fn from(permission: CreatePermission) -> Self { 77 | let now: DateTime = SystemTime::now().into(); 78 | 79 | Permission { 80 | id: ObjectId::new(), 81 | name: permission.name, 82 | description: permission.description, 83 | created_at: now, 84 | updated_at: now, 85 | } 86 | } 87 | } 88 | 89 | impl Display for Permission { 90 | /// # Summary 91 | /// 92 | /// Display the Permission. 93 | /// 94 | /// # Arguments 95 | /// 96 | /// * `f` - The Formatter. 97 | /// 98 | /// # Example 99 | /// 100 | /// ``` 101 | /// let permission = Permission { 102 | /// id: String::from("id"), 103 | /// name: String::from("Permission Name"), 104 | /// description: Some(String::from("Permission Description")), 105 | /// created_at: String::from("2021-01-01T00:00:00.000Z"), 106 | /// updated_at: String::from("2021-01-01T00:00:00.000Z"), 107 | /// }; 108 | /// 109 | /// println!("{}", permission); 110 | /// ``` 111 | /// 112 | /// # Returns 113 | /// 114 | /// * `std::fmt::Result` - The result of the operation. 115 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 116 | write!( 117 | f, 118 | "Permission: {{ id: {}, name: {}, description: {}, created_at: {}, updated_at: {} }}", 119 | self.id.to_hex(), 120 | self.name, 121 | self.description.as_ref().unwrap_or(&String::from("None")), 122 | self.created_at, 123 | self.updated_at 124 | ) 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/repository/permission/permission_repository.rs: -------------------------------------------------------------------------------- 1 | use crate::repository::audit::audit_repository::Error as AuditError; 2 | use crate::repository::permission::permission_model::Permission; 3 | use crate::repository::role::role_repository::Error as RoleError; 4 | use crate::services::role::role_service::RoleService; 5 | use chrono::{DateTime, Utc}; 6 | use futures::TryStreamExt; 7 | use mongodb::bson::doc; 8 | use mongodb::bson::oid::ObjectId; 9 | use mongodb::bson::Bson; 10 | use mongodb::options::FindOptions; 11 | use mongodb::{error::Error as MongoError, Database}; 12 | use std::fmt; 13 | use std::fmt::Debug; 14 | use std::time::SystemTime; 15 | 16 | #[derive(Clone)] 17 | pub struct PermissionRepository { 18 | pub collection: String, 19 | } 20 | 21 | #[derive(Clone, Debug)] 22 | pub enum Error { 23 | InvalidId(String), 24 | EmptyCollection, 25 | EmptyId, 26 | EmptyName, 27 | EmptyTextSearch, 28 | NameAlreadyTaken, 29 | PermissionNotFound(String), 30 | MongoDb(MongoError), 31 | Role(RoleError), 32 | Audit(AuditError), 33 | } 34 | 35 | impl fmt::Display for Error { 36 | /// # Summary 37 | /// 38 | /// Display the error message. 39 | /// 40 | /// # Arguments 41 | /// 42 | /// * `f` - The formatter. 43 | /// 44 | /// # Example 45 | /// 46 | /// ``` 47 | /// let error = Error::EmptyCollection; 48 | /// println!("{}", error); 49 | /// ``` 50 | /// 51 | /// # Returns 52 | /// 53 | /// * `fmt::Result` - The result of the display. 54 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 55 | match &self { 56 | Error::InvalidId(id) => write!(f, "Invalid Permission ID: {}", id), 57 | Error::EmptyCollection => write!(f, "Empty collection"), 58 | Error::EmptyId => write!(f, "Empty Permission ID"), 59 | Error::EmptyName => write!(f, "Empty Permission name"), 60 | Error::EmptyTextSearch => write!(f, "Empty text search"), 61 | Error::NameAlreadyTaken => write!(f, "Permission name already taken"), 62 | Error::PermissionNotFound(id) => write!(f, "Permission not found: {}", id), 63 | Error::MongoDb(e) => write!(f, "MongoDB error: {}", e), 64 | Error::Role(e) => write!(f, "Role error: {}", e), 65 | Error::Audit(e) => write!(f, "Audit error: {}", e), 66 | } 67 | } 68 | } 69 | 70 | impl PermissionRepository { 71 | /// # Summary 72 | /// 73 | /// Create a new PermissionRepository. 74 | /// 75 | /// # Arguments 76 | /// 77 | /// * `collection` - The name of the collection. 78 | /// 79 | /// # Example 80 | /// 81 | /// ``` 82 | /// let permission_repository = PermissionRepository::new(String::from("permissions")); 83 | /// ``` 84 | /// 85 | /// # Returns 86 | /// 87 | /// * `PermissionRepository` - The new PermissionRepository. 88 | pub fn new(collection: String) -> Result { 89 | if collection.is_empty() { 90 | return Err(Error::EmptyCollection); 91 | } 92 | 93 | Ok(PermissionRepository { collection }) 94 | } 95 | 96 | /// # Summary 97 | /// 98 | /// Create a new Permission. 99 | /// 100 | /// # Arguments 101 | /// 102 | /// * `permission` - The permission to create. 103 | /// * `db` - The database to use. 104 | /// 105 | /// # Example 106 | /// 107 | /// ``` 108 | /// let permission_repository = PermissionRepository::new(String::from("permissions")); 109 | /// let permission = Permission::from(CreatePermission { 110 | /// name: String::from("Permission Name"), 111 | /// description: Some(String::from("Permission Description")), 112 | /// }); 113 | /// 114 | /// let permission = permission_repository.create(permission, &db).await; 115 | /// ``` 116 | /// 117 | /// # Returns 118 | /// 119 | /// * `Result` - The result of the creation. 120 | pub async fn create(&self, permission: Permission, db: &Database) -> Result { 121 | match self.find_by_name(&permission.name.to_lowercase(), db).await { 122 | Ok(p) => { 123 | if p.is_some() { 124 | return Err(Error::NameAlreadyTaken); 125 | } 126 | } 127 | Err(e) => return Err(e), 128 | }; 129 | 130 | let permission_id = permission.id.to_hex(); 131 | 132 | match db 133 | .collection::(&self.collection) 134 | .insert_one(permission, None) 135 | .await 136 | { 137 | Ok(r) => r, 138 | Err(e) => return Err(Error::MongoDb(e)), 139 | }; 140 | 141 | let r = self.find_by_id(&permission_id, db).await; 142 | match r { 143 | Ok(r) => { 144 | if r.is_some() { 145 | Ok(r.unwrap()) 146 | } else { 147 | Err(Error::PermissionNotFound(permission_id)) 148 | } 149 | } 150 | Err(e) => Err(e), 151 | } 152 | } 153 | 154 | /// # Summary 155 | /// 156 | /// Find all Permission entities 157 | /// 158 | /// # Arguments 159 | /// 160 | /// * `db` - The database to use. 161 | /// * `limit` - The limit of Permissions to find. 162 | /// * `page` - The page of Permissions to find. 163 | /// 164 | /// # Example 165 | /// 166 | /// ``` 167 | /// let permission_repository = PermissionRepository::new(String::from("permissions")); 168 | /// let permissions = permission_repository.find_all(Some(10), Some(1), &db).await; 169 | /// ``` 170 | /// 171 | /// # Returns 172 | /// 173 | /// * `Result, Error>` - The result of the operation. 174 | pub async fn find_all( 175 | &self, 176 | limit: Option, 177 | page: Option, 178 | db: &Database, 179 | ) -> Result, Error> { 180 | let mut skip: Option = None; 181 | 182 | if let Some(l) = limit { 183 | if l > 1 { 184 | if let Some(p) = page { 185 | if p > 1 { 186 | let res = u64::try_from((p - 1) * l).unwrap_or(0); 187 | skip = Some(res); 188 | } 189 | } 190 | } 191 | } 192 | 193 | let find_options = FindOptions::builder().limit(limit).skip(skip).build(); 194 | 195 | let cursor = match db 196 | .collection::(&self.collection) 197 | .find(None, find_options) 198 | .await 199 | { 200 | Ok(d) => d, 201 | Err(e) => return Err(Error::MongoDb(e)), 202 | }; 203 | 204 | Ok(cursor.try_collect().await.unwrap_or_else(|_| vec![])) 205 | } 206 | 207 | /// # Summary 208 | /// 209 | /// Find a vector of Permissions by their ID. 210 | /// 211 | /// # Arguments 212 | /// 213 | /// * `id_vec` - The vector of IDs to find. 214 | /// * `db` - The database to use. 215 | /// 216 | /// # Example 217 | /// 218 | /// ``` 219 | /// let permission_repository = PermissionRepository::new(String::from("permissions")); 220 | /// let permissions = permission_repository.find_by_id_vec(vec![String::from("permission_id")], &db).await; 221 | /// ``` 222 | /// 223 | /// # Returns 224 | /// 225 | /// * `Result, Error>` - The result of the operation. 226 | pub async fn find_by_id_vec( 227 | &self, 228 | id_vec: Vec, 229 | db: &Database, 230 | ) -> Result, Error> { 231 | let mut oid_vec: Vec = vec![]; 232 | for id in id_vec { 233 | match ObjectId::parse_str(&id) { 234 | Ok(oid) => oid_vec.push(oid), 235 | Err(e) => return Err(Error::InvalidId(e.to_string())), 236 | } 237 | } 238 | 239 | if oid_vec.is_empty() { 240 | return Ok(vec![]); 241 | } 242 | 243 | let filter = doc! { 244 | "_id": { 245 | "$in": oid_vec, 246 | }, 247 | }; 248 | 249 | let cursor = match db 250 | .collection::(&self.collection) 251 | .find(filter, None) 252 | .await 253 | { 254 | Ok(d) => d, 255 | Err(e) => return Err(Error::MongoDb(e)), 256 | }; 257 | 258 | Ok(cursor.try_collect().await.unwrap_or_else(|_| vec![])) 259 | } 260 | 261 | /// # Summary 262 | /// 263 | /// Find a Permission by its ID. 264 | /// 265 | /// # Arguments 266 | /// 267 | /// * `id` - The ID of the Permission to find. 268 | /// * `db` - The database to use. 269 | /// 270 | /// # Example 271 | /// 272 | /// ``` 273 | /// let permission_repository = PermissionRepository::new(String::from("permissions")); 274 | /// let permission = permission_repository.find_by_id(String::from("permission_id"), &db).await; 275 | /// 276 | /// match permission { 277 | /// Ok(p) => println!("Permission: {:?}", p), 278 | /// Err(e) => println!("Error: {:?}", e), 279 | /// } 280 | /// ``` 281 | /// 282 | /// # Returns 283 | /// 284 | /// * `Result, Error>` - The result of the operation. 285 | pub async fn find_by_id(&self, id: &str, db: &Database) -> Result, Error> { 286 | if id.is_empty() { 287 | return Err(Error::EmptyId); 288 | } 289 | 290 | let target_object_id = match ObjectId::parse_str(id) { 291 | Ok(res) => res, 292 | Err(e) => { 293 | return Err(Error::InvalidId(e.to_string())); 294 | } 295 | }; 296 | 297 | let filter = doc! { 298 | "_id": target_object_id, 299 | }; 300 | 301 | let permission = match db 302 | .collection::(&self.collection) 303 | .find_one(filter, None) 304 | .await 305 | { 306 | Ok(d) => d, 307 | Err(e) => return Err(Error::MongoDb(e)), 308 | }; 309 | 310 | Ok(permission) 311 | } 312 | 313 | /// # Summary 314 | /// 315 | /// Find a Permission by its name. 316 | /// 317 | /// # Arguments 318 | /// 319 | /// * `name` - The name of the Permission to find. 320 | /// * `db` - The database to use. 321 | /// 322 | /// # Example 323 | /// 324 | /// ``` 325 | /// let permission_repository = PermissionRepository::new(String::from("permissions")); 326 | /// let permission = permission_repository.find_by_name(String::from("permission_name"), &db).await; 327 | /// 328 | /// match permission { 329 | /// Ok(p) => println!("Permission: {:?}", p), 330 | /// Err(e) => println!("Error: {:?}", e), 331 | /// } 332 | /// ``` 333 | /// 334 | /// # Returns 335 | /// 336 | /// * `Result, Error>` - The result of the operation. 337 | pub async fn find_by_name( 338 | &self, 339 | name: &str, 340 | db: &Database, 341 | ) -> Result, Error> { 342 | if name.is_empty() { 343 | return Err(Error::EmptyName); 344 | } 345 | 346 | let regex_pattern = format!("^{}$", regex::escape(name)); 347 | let re = mongodb::bson::Regex { 348 | pattern: regex_pattern, 349 | options: String::from("i"), 350 | }; 351 | 352 | let filter = doc! { "name": { "$regex": Bson::RegularExpression(re) } }; 353 | 354 | let permission = match db 355 | .collection::(&self.collection) 356 | .find_one(filter, None) 357 | .await 358 | { 359 | Ok(d) => d, 360 | Err(e) => return Err(Error::MongoDb(e)), 361 | }; 362 | 363 | Ok(permission) 364 | } 365 | 366 | /// # Summary 367 | /// 368 | /// Update a Permission. 369 | /// 370 | /// # Arguments 371 | /// 372 | /// * `permission` - The Permission to update. 373 | /// * `db` - The database to use. 374 | /// 375 | /// # Example 376 | /// 377 | /// ``` 378 | /// let permission_repository = PermissionRepository::new(String::from("permissions")); 379 | /// let permission = permission_repository.find_by_id(String::from("permission_id"), &db).await; 380 | /// 381 | /// match permission { 382 | /// Ok(p) => { 383 | /// let mut permission = p; 384 | /// permission.name = String::from("new_permission_name"); 385 | /// permission_repository.update(permission, &db).await; 386 | /// }, 387 | /// Err(e) => println!("Error: {:?}", e), 388 | /// } 389 | /// ``` 390 | /// 391 | /// # Returns 392 | /// 393 | /// * `Result` - The result of the operation. 394 | pub async fn update(&self, permission: Permission, db: &Database) -> Result { 395 | // Check if the name is already taken 396 | match self.find_by_name(&permission.name.to_lowercase(), db).await { 397 | Ok(p) => { 398 | if let Some(p) = p { 399 | if p.id != permission.id { 400 | return Err(Error::NameAlreadyTaken); 401 | } 402 | } 403 | } 404 | Err(e) => return Err(e), 405 | }; 406 | 407 | let permission_id = permission.id; 408 | let filter = doc! { 409 | "_id": &permission_id, 410 | }; 411 | 412 | let now: DateTime = SystemTime::now().into(); 413 | let now: String = now.to_rfc3339(); 414 | 415 | let update = doc! { 416 | "$set": { 417 | "name": permission.name, 418 | "description": permission.description, 419 | "updated_at": now, 420 | } 421 | }; 422 | 423 | let permission = match db 424 | .collection::(&self.collection) 425 | .find_one_and_update(filter, update, None) 426 | .await 427 | { 428 | Ok(d) => d, 429 | Err(e) => return Err(Error::MongoDb(e)), 430 | }; 431 | 432 | if permission.is_none() { 433 | return Err(Error::PermissionNotFound(permission_id.to_hex())); 434 | } 435 | 436 | Ok(permission.unwrap()) 437 | } 438 | 439 | /// # Summary 440 | /// 441 | /// Delete a Permission. 442 | /// 443 | /// # Arguments 444 | /// 445 | /// * `id` - The ID of the Permission to delete. 446 | /// * `db` - The database to use. 447 | /// * `role_service` - The reference RoleService to use. 448 | /// 449 | /// # Example 450 | /// 451 | /// ``` 452 | /// let permission_repository = PermissionRepository::new(String::from("permissions")); 453 | /// permission_repository.delete(String::from("permission_id"), &db, role_repository).await; 454 | /// ``` 455 | /// 456 | /// # Returns 457 | /// 458 | /// * `Result<(), Error>` - The result of the operation. 459 | pub async fn delete( 460 | &self, 461 | id: &str, 462 | db: &Database, 463 | role_service: &RoleService, 464 | ) -> Result<(), Error> { 465 | if id.is_empty() { 466 | return Err(Error::EmptyId); 467 | } 468 | 469 | let target_object_id = match ObjectId::parse_str(id) { 470 | Ok(res) => res, 471 | Err(e) => { 472 | return Err(Error::InvalidId(e.to_string())); 473 | } 474 | }; 475 | 476 | let filter = doc! { 477 | "_id": target_object_id, 478 | }; 479 | 480 | match db 481 | .collection::(&self.collection) 482 | .delete_one(filter, None) 483 | .await 484 | { 485 | Ok(_) => { 486 | match role_service.delete_permission_from_all_roles(id, db).await { 487 | Ok(_) => (), 488 | Err(e) => return Err(Error::Role(e)), 489 | }; 490 | } 491 | Err(e) => return Err(Error::MongoDb(e)), 492 | }; 493 | 494 | Ok(()) 495 | } 496 | 497 | /// # Summary 498 | /// 499 | /// Search for Permissions. 500 | /// 501 | /// # Arguments 502 | /// 503 | /// * `text` - The text to search for. 504 | /// * `limit` - The limit of Permissions to find. 505 | /// * `page` - The page of Permissions to find. 506 | /// * `db` - The database to use. 507 | /// 508 | /// # Example 509 | /// 510 | /// ``` 511 | /// let permission_repository = PermissionRepository::new(String::from("permissions")); 512 | /// let permissions = permission_repository.search(String::from("permission_name"), &db).await; 513 | /// ``` 514 | /// 515 | /// # Returns 516 | /// 517 | /// * `Result, Error>` - The result of the operation. 518 | pub async fn search( 519 | &self, 520 | text: &str, 521 | limit: Option, 522 | page: Option, 523 | db: &Database, 524 | ) -> Result, Error> { 525 | if text.is_empty() { 526 | return Err(Error::EmptyTextSearch); 527 | } 528 | 529 | let mut skip: Option = None; 530 | 531 | if let Some(l) = limit { 532 | if l > 1 { 533 | if let Some(p) = page { 534 | if p > 1 { 535 | let res = u64::try_from((p - 1) * l).unwrap_or(0); 536 | skip = Some(res); 537 | } 538 | } 539 | } 540 | } 541 | 542 | let find_options = FindOptions::builder().limit(limit).skip(skip).build(); 543 | 544 | let filter = doc! { 545 | "$text": { 546 | "$search": text, 547 | }, 548 | }; 549 | 550 | let cursor = match db 551 | .collection::(&self.collection) 552 | .find(filter, find_options) 553 | .await 554 | { 555 | Ok(d) => d, 556 | Err(e) => return Err(Error::MongoDb(e)), 557 | }; 558 | 559 | Ok(cursor.try_collect().await.unwrap_or_else(|_| vec![])) 560 | } 561 | } 562 | -------------------------------------------------------------------------------- /src/repository/role.rs: -------------------------------------------------------------------------------- 1 | pub mod role_model; 2 | pub mod role_repository; 3 | -------------------------------------------------------------------------------- /src/repository/role/role_model.rs: -------------------------------------------------------------------------------- 1 | use crate::web::dto::role::create_role::CreateRole; 2 | use chrono::{DateTime, Utc}; 3 | use mongodb::bson::oid::ObjectId; 4 | use serde::{Deserialize, Serialize}; 5 | use std::fmt::{Display, Formatter}; 6 | use std::time::SystemTime; 7 | 8 | #[derive(Serialize, Deserialize, Clone)] 9 | pub struct Role { 10 | #[serde(rename = "_id")] 11 | pub id: ObjectId, 12 | pub name: String, 13 | pub description: Option, 14 | pub permissions: Option>, 15 | #[serde(with = "mongodb::bson::serde_helpers::chrono_datetime_as_bson_datetime")] 16 | #[serde(rename = "createdAt")] 17 | pub created_at: DateTime, 18 | #[serde(with = "mongodb::bson::serde_helpers::chrono_datetime_as_bson_datetime")] 19 | #[serde(rename = "updatedAt")] 20 | pub updated_at: DateTime, 21 | } 22 | 23 | impl Role { 24 | /// # Summary 25 | /// 26 | /// Create a new Role. 27 | /// 28 | /// # Arguments 29 | /// 30 | /// * `name` - The name of the Role. 31 | /// * `description` - The description of the Role. 32 | /// * `permissions` - The Permissions of the Role. 33 | /// 34 | /// # Example 35 | /// 36 | /// ``` 37 | /// let role = Role::new(String::from("Role Name"), Some(String::from("Role Description")), Some(vec![ObjectId::new()])); 38 | /// ``` 39 | /// 40 | /// # Returns 41 | /// 42 | /// * `Role` - The new Role. 43 | pub fn new( 44 | name: String, 45 | description: Option, 46 | permissions: Option>, 47 | ) -> Self { 48 | let now: DateTime = SystemTime::now().into(); 49 | 50 | Role { 51 | id: ObjectId::new(), 52 | name, 53 | description, 54 | permissions, 55 | created_at: now, 56 | updated_at: now, 57 | } 58 | } 59 | } 60 | 61 | impl From for Role { 62 | /// # Summary 63 | /// 64 | /// Convert a CreateRole into a Role without the Permission entities. 65 | /// 66 | /// # Arguments 67 | /// 68 | /// * `create_role` - The CreateRole to convert. 69 | /// 70 | /// # Example 71 | /// 72 | /// ``` 73 | /// let create_role = CreateRole { 74 | /// name: String::from("name"), 75 | /// description: Some(String::from("description")), 76 | /// permissions: Some(vec![String::from("permission")]), 77 | /// }; 78 | /// 79 | /// let role = Role::from(create_role); 80 | /// ``` 81 | /// 82 | /// # Returns 83 | /// 84 | /// * `Role` - The new Role. 85 | fn from(create_role: CreateRole) -> Self { 86 | let now: DateTime = SystemTime::now().into(); 87 | 88 | let permissions = match create_role.permissions { 89 | None => None, 90 | Some(p) => { 91 | let mut oid_vec: Vec = vec![]; 92 | for oid in p { 93 | match ObjectId::parse_str(&oid) { 94 | Ok(d) => oid_vec.push(d), 95 | Err(_) => continue, 96 | } 97 | } 98 | Some(oid_vec) 99 | } 100 | }; 101 | 102 | Role { 103 | id: ObjectId::new(), 104 | name: create_role.name, 105 | description: create_role.description, 106 | permissions, 107 | created_at: now, 108 | updated_at: now, 109 | } 110 | } 111 | } 112 | 113 | impl Display for Role { 114 | /// # Summary 115 | /// 116 | /// Display the Role. 117 | /// 118 | /// # Arguments 119 | /// 120 | /// * `f` - The Formatter. 121 | /// 122 | /// # Example 123 | /// 124 | /// ``` 125 | /// let role = Role { 126 | /// id: String::from("id"), 127 | /// name: String::from("name"), 128 | /// description: Some(String::from("description")), 129 | /// permissions: Some(vec![String::from("permission")]), 130 | /// created_at: String::from("created_at"), 131 | /// updated_at: String::from("updated_at"), 132 | /// }; 133 | /// println!("{}", role); 134 | /// ``` 135 | /// 136 | /// # Returns 137 | /// 138 | /// * `std::fmt::Result` - The result of the display. 139 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 140 | write!(f, "Role: {{ id: {}, name: {}, description: {}, permissions: {:?}, created_at: {}, updated_at: {} }}", self.id.to_hex(), self.name, self.description.as_ref().unwrap_or(&String::from("None")), self.permissions.as_ref().unwrap_or(&vec![]), self.created_at, self.updated_at) 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/repository/user.rs: -------------------------------------------------------------------------------- 1 | pub mod user_model; 2 | pub mod user_repository; 3 | -------------------------------------------------------------------------------- /src/repository/user/user_model.rs: -------------------------------------------------------------------------------- 1 | use crate::web::dto::authentication::register_request::RegisterRequest; 2 | use crate::web::dto::user::create_user::CreateUser; 3 | use chrono::{DateTime, Utc}; 4 | use mongodb::bson::oid::ObjectId; 5 | use serde::{Deserialize, Serialize}; 6 | use std::fmt::{Display, Formatter}; 7 | use std::time::SystemTime; 8 | 9 | #[derive(Serialize, Deserialize, Clone)] 10 | pub struct User { 11 | #[serde(rename = "_id")] 12 | pub id: ObjectId, 13 | pub username: String, 14 | pub email: Option, 15 | #[serde(rename = "firstName")] 16 | pub first_name: Option, 17 | #[serde(rename = "lastName")] 18 | pub last_name: Option, 19 | pub password: String, 20 | pub roles: Option>, 21 | #[serde(with = "mongodb::bson::serde_helpers::chrono_datetime_as_bson_datetime")] 22 | #[serde(rename = "createdAt")] 23 | pub created_at: DateTime, 24 | #[serde(with = "mongodb::bson::serde_helpers::chrono_datetime_as_bson_datetime")] 25 | #[serde(rename = "updatedAt")] 26 | pub updated_at: DateTime, 27 | pub enabled: bool, 28 | } 29 | 30 | impl User { 31 | /// # Summary 32 | /// 33 | /// Create a new User. 34 | /// 35 | /// # Arguments 36 | /// 37 | /// * `username` - The username of the User. 38 | /// * `email` - The email of the User. 39 | /// * `first_name` - The first name of the User. 40 | /// * `last_name` - The last name of the User. 41 | /// * `password` - The password of the User. 42 | /// * `roles` - The roles of the User. 43 | /// * `enabled` - The enabled of the User. 44 | /// 45 | /// # Example 46 | /// 47 | /// ``` 48 | /// let user = User::new( 49 | /// String::from("username"), 50 | /// Some(String::from("email")), 51 | /// String::from("first_name"), 52 | /// String::from("last_name"), 53 | /// String::from("password"), 54 | /// Some(vec![String::from("role")]), 55 | /// true, 56 | /// ); 57 | /// ``` 58 | /// 59 | /// # Returns 60 | /// 61 | /// * `User` - The new User. 62 | pub fn new( 63 | username: String, 64 | email: Option, 65 | first_name: Option, 66 | last_name: Option, 67 | password: String, 68 | roles: Option>, 69 | enabled: bool, 70 | ) -> User { 71 | let now: DateTime = SystemTime::now().into(); 72 | 73 | let roles: Option> = match roles { 74 | None => None, 75 | Some(r) => { 76 | let mut oid_vec: Vec = vec![]; 77 | for role in r { 78 | match ObjectId::parse_str(&role) { 79 | Ok(oid) => oid_vec.push(oid), 80 | Err(_) => continue, 81 | } 82 | } 83 | Some(oid_vec) 84 | } 85 | }; 86 | 87 | User { 88 | id: ObjectId::new(), 89 | username, 90 | email, 91 | first_name, 92 | last_name, 93 | password, 94 | roles, 95 | created_at: now, 96 | updated_at: now, 97 | enabled, 98 | } 99 | } 100 | } 101 | 102 | impl From for User { 103 | /// # Summary 104 | /// 105 | /// Convert a CreateUser into a User. 106 | /// 107 | /// # Arguments 108 | /// 109 | /// * `value` - The CreateUser to convert. 110 | /// 111 | /// # Example 112 | /// 113 | /// ``` 114 | /// let create_user = CreateUser { 115 | /// username: String::from("username"), 116 | /// email: Some(String::from("email")), 117 | /// first_name: String::from("first_name"), 118 | /// last_name: String::from("last_name"), 119 | /// password: String::from("password"), 120 | /// roles: Some(vec![String::from("role")]), 121 | /// }; 122 | /// 123 | /// let user = User::from(create_user); 124 | /// ``` 125 | /// 126 | /// # Returns 127 | /// 128 | /// * `User` - The new User. 129 | fn from(value: CreateUser) -> Self { 130 | let now: DateTime = SystemTime::now().into(); 131 | 132 | let roles: Option> = match value.roles { 133 | None => None, 134 | Some(r) => { 135 | let mut oid_vec: Vec = vec![]; 136 | for role in r { 137 | match ObjectId::parse_str(&role) { 138 | Ok(oid) => oid_vec.push(oid), 139 | Err(_) => continue, 140 | } 141 | } 142 | Some(oid_vec) 143 | } 144 | }; 145 | 146 | User { 147 | id: ObjectId::new(), 148 | username: value.username, 149 | email: value.email, 150 | first_name: value.first_name, 151 | last_name: value.last_name, 152 | password: value.password, 153 | roles, 154 | created_at: now, 155 | updated_at: now, 156 | enabled: true, 157 | } 158 | } 159 | } 160 | 161 | impl From for User { 162 | /// # Summary 163 | /// 164 | /// Convert a RegisterRequest into a User. 165 | /// 166 | /// # Arguments 167 | /// 168 | /// * `value` - The RegisterRequest to convert. 169 | /// 170 | /// # Example 171 | /// 172 | /// ``` 173 | /// let register_request = RegisterRequest { 174 | /// username: String::from("username"), 175 | /// email: Some(String::from("email")), 176 | /// first_name: String::from("first_name"), 177 | /// last_name: String::from("last_name"), 178 | /// password: String::from("password"), 179 | /// }; 180 | /// 181 | /// let user = User::from(register_request); 182 | /// ``` 183 | /// 184 | /// # Returns 185 | /// 186 | /// * `User` - The new User. 187 | fn from(value: RegisterRequest) -> Self { 188 | let now: DateTime = SystemTime::now().into(); 189 | 190 | User { 191 | id: ObjectId::new(), 192 | username: value.username, 193 | email: value.email, 194 | first_name: value.first_name, 195 | last_name: value.last_name, 196 | password: value.password, 197 | roles: None, 198 | created_at: now, 199 | updated_at: now, 200 | enabled: true, 201 | } 202 | } 203 | } 204 | 205 | impl Display for User { 206 | /// # Summary 207 | /// 208 | /// Display the User. 209 | /// 210 | /// # Arguments 211 | /// 212 | /// * `f` - The Formatter. 213 | /// 214 | /// # Example 215 | /// 216 | /// ``` 217 | /// let user = User { 218 | /// id: String::from("id"), 219 | /// username: String::from("username"), 220 | /// email: Some(String::from("email")), 221 | /// first_name: String::from("first_name"), 222 | /// last_name: String::from("last_name"), 223 | /// password: String::from("password"), 224 | /// roles: Some(vec![String::from("role")]), 225 | /// created_at: String::from("created_at"), 226 | /// updated_at: String::from("updated_at"), 227 | /// enabled: true, 228 | /// }; 229 | /// 230 | /// println!("{}", user); 231 | /// ``` 232 | /// 233 | /// # Returns 234 | /// 235 | /// * `std::fmt::Result` - The result of the operation. 236 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 237 | write!( 238 | f, 239 | "User: [id: {}, username: {}, email: {}, first_name: {}, last_name: {}, password: {}, roles: {:?}, created_at: {}, updated_at: {}, enabled: {}]", 240 | self.id.to_hex(), 241 | self.username, 242 | match &self.email { 243 | None => String::from("None"), 244 | Some(e) => e.to_string(), 245 | }, 246 | self.first_name.clone().unwrap_or(String::from("")), 247 | self.last_name.clone().unwrap_or(String::from("")), 248 | self.password, 249 | match &self.roles { 250 | None => String::from("None"), 251 | Some(r) => format!("{:?}", r) 252 | }, 253 | self.created_at, 254 | self.updated_at, 255 | self.enabled, 256 | ) 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /src/services.rs: -------------------------------------------------------------------------------- 1 | use crate::services::audit::audit_service::AuditService; 2 | use crate::services::jwt::jwt_service::JwtService; 3 | use crate::services::permission::permission_service::PermissionService; 4 | use crate::services::role::role_service::RoleService; 5 | use crate::services::user::user_service::UserService; 6 | 7 | pub mod audit; 8 | pub mod jwt; 9 | pub mod password; 10 | pub mod permission; 11 | pub mod role; 12 | pub mod user; 13 | 14 | #[derive(Clone)] 15 | pub struct Services { 16 | pub permission_service: PermissionService, 17 | pub role_service: RoleService, 18 | pub user_service: UserService, 19 | pub jwt_service: JwtService, 20 | pub audit_service: AuditService, 21 | } 22 | 23 | impl Services { 24 | /// # Summary 25 | /// 26 | /// Create a new instance of Services. 27 | /// 28 | /// # Arguments 29 | /// 30 | /// * `permission_service` - The PermissionService. 31 | /// * `role_service` - The RoleService. 32 | /// * `user_service` - The UserService. 33 | /// * `jwt_service` - The JwtService. 34 | /// * `audit_service` - The AuditService. 35 | /// 36 | /// # Returns 37 | /// 38 | /// A new instance of Services. 39 | pub fn new( 40 | permission_service: PermissionService, 41 | role_service: RoleService, 42 | user_service: UserService, 43 | jwt_service: JwtService, 44 | audit_service: AuditService, 45 | ) -> Services { 46 | Services { 47 | permission_service, 48 | role_service, 49 | user_service, 50 | jwt_service, 51 | audit_service, 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/services/audit.rs: -------------------------------------------------------------------------------- 1 | pub mod audit_service; 2 | -------------------------------------------------------------------------------- /src/services/audit/audit_service.rs: -------------------------------------------------------------------------------- 1 | use crate::repository::audit::audit_model::Audit; 2 | use crate::repository::audit::audit_repository::{AuditRepository, Error}; 3 | use log::info; 4 | use mongodb::Database; 5 | 6 | #[derive(Clone)] 7 | pub struct AuditService { 8 | pub audit_repository: AuditRepository, 9 | pub enabled: bool, 10 | } 11 | 12 | impl AuditService { 13 | /// # Summary 14 | /// 15 | /// Create a new AuditService. 16 | /// 17 | /// # Arguments 18 | /// 19 | /// * `audit_repository` - The AuditRepository. 20 | /// * `enabled` - Whether or not the AuditService is enabled. 21 | /// 22 | /// # Returns 23 | /// 24 | /// * `AuditService` - The AuditService. 25 | pub fn new(audit_repository: AuditRepository, enabled: bool) -> AuditService { 26 | AuditService { 27 | audit_repository, 28 | enabled, 29 | } 30 | } 31 | 32 | /// # Summary 33 | /// 34 | /// Create a new Audit. 35 | /// 36 | /// # Arguments 37 | /// 38 | /// * `audit` - The Audit to create. 39 | /// * `db` - The Database to create the Audit in. 40 | /// 41 | /// # Returns 42 | /// 43 | /// * `Result<(), Error>` - The result of the operation. 44 | pub async fn create(&self, audit: Audit, db: &Database) -> Result<(), Error> { 45 | if !self.enabled { 46 | return Ok(()); 47 | } 48 | 49 | info!("Creating audit: {}", audit); 50 | self.audit_repository.create(audit, db).await 51 | } 52 | 53 | /// # Summary 54 | /// 55 | /// Find an Audit by id. 56 | /// 57 | /// # Arguments 58 | /// 59 | /// * `id` - The id of the Audit to find. 60 | /// * `db` - The Database to find the Audit in. 61 | /// 62 | /// # Returns 63 | /// 64 | /// * `Result, Error>` - The result of the operation. 65 | pub async fn find_by_id(&self, id: &str, db: &Database) -> Result, Error> { 66 | info!("Finding audit by ID: {}", id); 67 | self.audit_repository.find_by_id(id, db).await 68 | } 69 | 70 | /// # Summary 71 | /// 72 | /// Find all Audits. 73 | /// 74 | /// # Arguments 75 | /// 76 | /// * `limit` - The limit of Audits to find. 77 | /// * `page` - The page of Audits to find. 78 | /// * `db` - The Database to find the Audits in. 79 | /// 80 | /// # Returns 81 | /// 82 | /// * `Result, Error>` - The result of the operation. 83 | pub async fn find_all( 84 | &self, 85 | limit: Option, 86 | page: Option, 87 | db: &Database, 88 | ) -> Result, Error> { 89 | info!("Finding all audits"); 90 | self.audit_repository.find_all(limit, page, db).await 91 | } 92 | 93 | /// # Summary 94 | /// 95 | /// Search for Audit entities. 96 | /// 97 | /// # Arguments 98 | /// 99 | /// * `text` - The text to search for. 100 | /// * `limit` - The limit of Audits to find. 101 | /// * `page` - The page of Audits to find. 102 | /// * `db` - The database to use. 103 | /// 104 | /// # Example 105 | /// 106 | /// ``` 107 | /// let audit_repository = AuditRepository::new("audit".to_string()).unwrap(); 108 | /// let audit_service = AuditService::new(audit_repository, true); 109 | /// let db = mongodb::Client::with_uri_str("mongodb://localhost:27017") 110 | /// .unwrap() 111 | /// .database("test"); 112 | /// let res = audit_service.search("test", 1, 10, &db).await; 113 | /// ``` 114 | /// 115 | /// # Returns 116 | /// 117 | /// * `Result, Error>` - The result of the operation. 118 | pub async fn search( 119 | &self, 120 | text: &str, 121 | limit: Option, 122 | page: Option, 123 | db: &Database, 124 | ) -> Result, Error> { 125 | info!("Searching for audits: {}", text); 126 | self.audit_repository.search(text, limit, page, db).await 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/services/jwt.rs: -------------------------------------------------------------------------------- 1 | pub mod jwt_service; 2 | -------------------------------------------------------------------------------- /src/services/jwt/jwt_service.rs: -------------------------------------------------------------------------------- 1 | use crate::configuration::jwt_config::JwtConfig; 2 | use jsonwebtoken::{encode, DecodingKey, EncodingKey, Header}; 3 | use log::error; 4 | use serde::{Deserialize, Serialize}; 5 | use std::fmt::{Display, Formatter}; 6 | 7 | #[derive(Debug, Serialize, Deserialize)] 8 | pub struct Claims { 9 | exp: usize, 10 | iat: usize, 11 | sub: String, 12 | } 13 | 14 | impl Claims { 15 | /// # Summary 16 | /// 17 | /// Create a new Claims. 18 | /// 19 | /// # Arguments 20 | /// 21 | /// * `sub` - The subject of the Claims. 22 | /// * `exp` - The expiration time of the Claims. 23 | /// * `iat` - The issued at time of the Claims. 24 | pub fn new(sub: String, exp: usize, iat: usize) -> Claims { 25 | Claims { sub, exp, iat } 26 | } 27 | } 28 | 29 | pub enum Error { 30 | InvalidToken(String), 31 | } 32 | 33 | impl Display for Error { 34 | /// # Summary 35 | /// 36 | /// Display the Error. 37 | /// 38 | /// # Arguments 39 | /// 40 | /// * `f` - The Formatter. 41 | /// 42 | /// # Example 43 | /// 44 | /// ``` 45 | /// let error = Error::InvalidToken(String::from("message")); 46 | /// println!("{}", error); 47 | /// ``` 48 | /// 49 | /// # Returns 50 | /// 51 | /// * `std::fmt::Result` - The result of the operation. 52 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 53 | match self { 54 | Error::InvalidToken(message) => write!(f, "Invalid token: {}", message), 55 | } 56 | } 57 | } 58 | 59 | #[derive(Clone)] 60 | pub struct JwtService { 61 | pub jwt_config: JwtConfig, 62 | } 63 | 64 | impl JwtService { 65 | /// # Summary 66 | /// 67 | /// Create a new JwtService. 68 | /// 69 | /// # Arguments 70 | /// 71 | /// * `jwt_config` - The configuration for the JwtService. 72 | /// 73 | /// # Example 74 | /// 75 | /// ``` 76 | /// let jwt_service = JwtService::new(jwt_config); 77 | /// ``` 78 | /// 79 | /// # Returns 80 | /// 81 | /// * `JwtService` - The new JwtService. 82 | pub fn new(jwt_config: JwtConfig) -> JwtService { 83 | JwtService { jwt_config } 84 | } 85 | 86 | /// # Summary 87 | /// 88 | /// Generate a JWT token. 89 | /// 90 | /// # Arguments 91 | /// 92 | /// * `subject` - The subject of the JWT token. 93 | /// 94 | /// # Example 95 | /// 96 | /// ``` 97 | /// let token = jwt_service.generate_jwt_token("subject"); 98 | /// ``` 99 | /// 100 | /// # Returns 101 | /// 102 | /// * `Option` - The JWT token. 103 | pub fn generate_jwt_token(&self, subject: &str) -> Option { 104 | let now = chrono::Utc::now(); 105 | let exp = now + chrono::Duration::seconds(self.jwt_config.jwt_expiration as i64); 106 | let iat = now; 107 | 108 | let claims = Claims::new( 109 | String::from(subject), 110 | exp.timestamp() as usize, 111 | iat.timestamp() as usize, 112 | ); 113 | 114 | match encode( 115 | &Header::default(), 116 | &claims, 117 | &EncodingKey::from_secret(self.jwt_config.jwt_secret.as_bytes()), 118 | ) { 119 | Ok(t) => Some(t), 120 | Err(e) => { 121 | error!("Error generating JWT token: {}", e.to_string()); 122 | None 123 | } 124 | } 125 | } 126 | 127 | /// # Summary 128 | /// 129 | /// Verify a JWT token. 130 | /// 131 | /// # Arguments 132 | /// 133 | /// * `token` - The JWT token to verify. 134 | /// 135 | /// # Example 136 | /// 137 | /// ``` 138 | /// let sub = jwt_service.verify_jwt_token("token"); 139 | /// ``` 140 | /// 141 | /// # Returns 142 | /// 143 | /// * `Result` - The result of the operation. 144 | pub fn verify_jwt_token(&self, token: &str) -> Result { 145 | let token_data = jsonwebtoken::decode::( 146 | token, 147 | &DecodingKey::from_secret(self.jwt_config.jwt_secret.as_bytes()), 148 | &jsonwebtoken::Validation::default(), 149 | ); 150 | 151 | match token_data { 152 | Ok(t) => Ok(t.claims.sub), 153 | Err(e) => { 154 | error!("Error verifying JWT token: {}", e.to_string()); 155 | Err(Error::InvalidToken(e.to_string())) 156 | } 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/services/password.rs: -------------------------------------------------------------------------------- 1 | pub mod password_service; 2 | -------------------------------------------------------------------------------- /src/services/password/password_service.rs: -------------------------------------------------------------------------------- 1 | use argon2::{ 2 | password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString}, 3 | Argon2, 4 | }; 5 | 6 | #[derive(Clone)] 7 | pub struct PasswordService {} 8 | 9 | impl PasswordService { 10 | /// # Summary 11 | /// 12 | /// Hash a password. 13 | /// 14 | /// # Arguments 15 | /// 16 | /// * `password` - The password to hash. 17 | /// 18 | /// # Returns 19 | /// 20 | /// A Result containing the hashed password or an error. 21 | pub fn hash_password(password: String) -> Result { 22 | let password = &password.as_bytes(); 23 | let argon2 = Argon2::default(); 24 | 25 | let salt = SaltString::generate(&mut OsRng); 26 | 27 | match argon2.hash_password(password, &salt) { 28 | Ok(e) => Ok(e.to_string()), 29 | Err(e) => Err(e.to_string()), 30 | } 31 | } 32 | 33 | /// # Summary 34 | /// 35 | /// Verify a password. 36 | /// 37 | /// # Arguments 38 | /// 39 | /// * `password` - The password to verify. 40 | /// * `hash` - The hash to verify against. 41 | /// 42 | /// # Returns 43 | /// 44 | /// A boolean indicating whether the password is valid. 45 | pub fn verify_password(password: &str, hash: &PasswordHash) -> bool { 46 | Argon2::default() 47 | .verify_password(&password.as_bytes(), &hash) 48 | .is_ok() 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/services/permission.rs: -------------------------------------------------------------------------------- 1 | pub mod permission_service; 2 | -------------------------------------------------------------------------------- /src/services/permission/permission_service.rs: -------------------------------------------------------------------------------- 1 | use crate::repository::audit::audit_model::Action::{Create, Delete, Update}; 2 | use crate::repository::audit::audit_model::ResourceType::Permission as PermissionResourceType; 3 | use crate::repository::audit::audit_model::{Audit, ResourceIdType}; 4 | use crate::repository::audit::audit_repository::Error as AuditError; 5 | use crate::repository::permission::permission_model::Permission; 6 | use crate::repository::permission::permission_repository::{Error, PermissionRepository}; 7 | use crate::services::audit::audit_service::AuditService; 8 | use crate::services::role::role_service::RoleService; 9 | use log::{error, info}; 10 | use mongodb::bson::oid::ObjectId; 11 | use mongodb::Database; 12 | 13 | #[derive(Clone)] 14 | pub struct PermissionService { 15 | pub permission_repository: PermissionRepository, 16 | } 17 | 18 | impl PermissionService { 19 | /// # Summary 20 | /// 21 | /// Create a new PermissionService. 22 | /// 23 | /// # Arguments 24 | /// 25 | /// * `permission_repository` - The PermissionRepository to be used by the PermissionService. 26 | /// 27 | /// # Example 28 | /// 29 | /// ``` 30 | /// let permission_repository = PermissionRepository::new(String::from("permissions")); 31 | /// let permission_service = PermissionService::new(permission_repository); 32 | /// ``` 33 | /// 34 | /// # Returns 35 | /// 36 | /// * `PermissionService` - The new PermissionService. 37 | pub fn new(permission_repository: PermissionRepository) -> PermissionService { 38 | PermissionService { 39 | permission_repository, 40 | } 41 | } 42 | 43 | /// # Summary 44 | /// 45 | /// Create a new Permission entity. 46 | /// 47 | /// # Arguments 48 | /// 49 | /// * `new_permission` - The Permission entity to create. 50 | /// * `user_id` - The ID of the User creating the Permission entity. 51 | /// * `db` - The Database to create the Permission entity in. 52 | /// * `audit` - The AuditService to be used. 53 | /// 54 | /// # Example 55 | /// 56 | /// ``` 57 | /// let permission_repository = PermissionRepository::new(String::from("permissions")); 58 | /// let permission_service = PermissionService::new(permission_repository); 59 | /// let db = mongodb::Database::new(); 60 | /// let audit_service = AuditService::new(audit_repository); 61 | /// let user_id = ObjectId::parse_str("user_id").unwrap(); 62 | /// let permission = Permission::new(String::from("name"), String::from("description")); 63 | /// 64 | /// let new_permission = permission_service.create(permission, user_id, &db, &audit_service); 65 | /// ``` 66 | /// 67 | /// # Returns 68 | /// 69 | /// * `Option` - The Permission entity. 70 | /// * `Error` - The Error that occurred. 71 | pub async fn create( 72 | &self, 73 | new_permission: Permission, 74 | user_id: Option, 75 | db: &Database, 76 | audit: &AuditService, 77 | ) -> Result { 78 | info!("Creating Permission: {}", new_permission); 79 | 80 | if user_id.is_some() { 81 | let new_audit = Audit::new( 82 | user_id.unwrap(), 83 | Create, 84 | new_permission.id, 85 | ResourceIdType::PermissionId, 86 | PermissionResourceType, 87 | ); 88 | match audit.create(new_audit, db).await { 89 | Ok(_) => {} 90 | Err(e) => { 91 | error!("Failed to create Audit: {}", e); 92 | return Err(Error::Audit(e)); 93 | } 94 | } 95 | } 96 | 97 | self.permission_repository.create(new_permission, db).await 98 | } 99 | 100 | /// # Summary 101 | /// 102 | /// Find all Permission entities. 103 | /// 104 | /// # Arguments 105 | /// 106 | /// * `limit` - The limit of Permission entities to find. 107 | /// * `page` - The page of Permission entities to find. 108 | /// * `db` - The Database to find the Permission entities in. 109 | /// * `audit` - The AuditService to be used. 110 | /// 111 | /// # Example 112 | /// 113 | /// ``` 114 | /// let permission_repository = PermissionRepository::new(String::from("permissions")); 115 | /// let permission_service = PermissionService::new(permission_repository); 116 | /// let db = mongodb::Database::new(); 117 | /// let permissions = permission_service.find_all(limit, page, &db); 118 | /// ``` 119 | /// 120 | /// # Returns 121 | /// 122 | /// * `Vec` - The Permission entities. 123 | /// * `Error` - The Error that occurred. 124 | pub async fn find_all( 125 | &self, 126 | limit: Option, 127 | page: Option, 128 | db: &Database, 129 | ) -> Result, Error> { 130 | info!("Finding all permissions"); 131 | self.permission_repository.find_all(limit, page, db).await 132 | } 133 | 134 | /// # Summary 135 | /// 136 | /// Find all Permission entities by id. 137 | /// 138 | /// # Arguments 139 | /// 140 | /// * `id_vec` - The Vector of IDs of the Permission entities. 141 | /// * `user_id` - The ID of the User finding the Permission entities. 142 | /// * `db` - The Database to find the Permission entities in. 143 | /// * `audit` - The AuditService to be used. 144 | /// 145 | /// # Example 146 | /// 147 | /// ``` 148 | /// let permission_repository = PermissionRepository::new(String::from("permissions")); 149 | /// let permission_service = PermissionService::new(permission_repository); 150 | /// let db = mongodb::Database::new(); 151 | /// let id_vec = vec![String::from("id")]; 152 | /// let permissions = permission_service.find_by_id_vec(id_vec, &db); 153 | /// ``` 154 | /// 155 | /// # Returns 156 | /// 157 | /// * `Vec` - The Permission entities. 158 | /// * `Error` - The Error that occurred. 159 | pub async fn find_by_id_vec( 160 | &self, 161 | id_vec: Vec, 162 | db: &Database, 163 | ) -> Result, Error> { 164 | info!("Finding permissions by id_vec: {:?}", id_vec); 165 | self.permission_repository.find_by_id_vec(id_vec, db).await 166 | } 167 | 168 | /// # Summary 169 | /// 170 | /// Find a Permission entity by id. 171 | /// 172 | /// # Arguments 173 | /// 174 | /// * `id` - The id of the Permission entity. 175 | /// * `db` - The Database to be used. 176 | /// 177 | /// # Example 178 | /// 179 | /// ``` 180 | /// let permission_repository = PermissionRepository::new(String::from("permissions")); 181 | /// let permission_service = PermissionService::new(permission_repository); 182 | /// let db = mongodb::Database::new(); 183 | /// 184 | /// let permission = permission_service.find_by_id(String::from("id"), &db); 185 | /// ``` 186 | /// 187 | /// # Returns 188 | /// 189 | /// * `Option` - The Permission entity. 190 | /// * `Error` - The Error that occurred. 191 | pub async fn find_by_id(&self, id: &str, db: &Database) -> Result, Error> { 192 | info!("Finding Permission by ID: {}", id); 193 | self.permission_repository.find_by_id(id, db).await 194 | } 195 | 196 | /// # Summary 197 | /// 198 | /// Find a Permission by its name. 199 | /// 200 | /// # Arguments 201 | /// 202 | /// * `name` - The name of the Permission to find. 203 | /// * `user_id` - The ID of the User finding the Permission. 204 | /// * `db` - The database to use. 205 | /// * `audit` - The AuditService to be used. 206 | /// 207 | /// # Example 208 | /// 209 | /// ``` 210 | /// let permission_repository = PermissionRepository::new(String::from("permissions")); 211 | /// let permission_service = PermissionService::new(permission_repository); 212 | /// let db = mongodb::Database::new(); 213 | /// let name = String::from("name"); 214 | /// let permission = permission_service.find_by_name(name, &db); 215 | /// ``` 216 | /// 217 | /// # Returns 218 | /// 219 | /// * `Result, Error>` - The result of the operation. 220 | pub async fn find_by_name( 221 | &self, 222 | name: &str, 223 | db: &Database, 224 | ) -> Result, Error> { 225 | info!("Finding Permission by name: {}", name); 226 | self.permission_repository.find_by_name(name, db).await 227 | } 228 | 229 | /// # Summary 230 | /// 231 | /// Update a Permission entity. 232 | /// 233 | /// # Arguments 234 | /// 235 | /// * `permission` - The Permission entity to create. 236 | /// * `user_id` - The ID of the User updating the Permission. 237 | /// * `db` - The Database to be used. 238 | /// * `audit` - The AuditService to be used. 239 | /// 240 | /// # Example 241 | /// 242 | /// ``` 243 | /// let permission_repository = PermissionRepository::new(String::from("permissions")); 244 | /// let permission_service = PermissionService::new(permission_repository); 245 | /// let db = mongodb::Database::new(); 246 | /// let audit_service = AuditService::new(audit_repository); 247 | /// let user_id = ObjectId::parse_str("user_id").unwrap(); 248 | /// let permission = Permission::new(String::from("name"), String::from("description")); 249 | /// let updated_permission = permission_service.update(permission, user_id, &db, &audit_service); 250 | /// ``` 251 | /// 252 | /// # Returns 253 | /// 254 | /// * `Permission` - The Permission entity. 255 | /// * `Error` - The Error that occurred. 256 | pub async fn update( 257 | &self, 258 | permission: Permission, 259 | user_id: Option, 260 | db: &Database, 261 | audit: &AuditService, 262 | ) -> Result { 263 | info!("Updating Permission: {}", permission); 264 | 265 | if user_id.is_some() { 266 | let new_audit = Audit::new( 267 | user_id.unwrap(), 268 | Update, 269 | permission.id, 270 | ResourceIdType::PermissionId, 271 | PermissionResourceType, 272 | ); 273 | match audit.create(new_audit, db).await { 274 | Ok(_) => {} 275 | Err(e) => { 276 | error!("Failed to create Audit: {}", e); 277 | return Err(Error::Audit(e)); 278 | } 279 | } 280 | } 281 | 282 | self.permission_repository.update(permission, db).await 283 | } 284 | 285 | /// # Summary 286 | /// 287 | /// Delete a Permission entity. 288 | /// 289 | /// # Arguments 290 | /// 291 | /// * `id` - The id of the Permission entity to delete. 292 | /// * `user_id` - The ID of the User deleting the Permission. 293 | /// * `db` - The Database to be used. 294 | /// * `role_service` - The RoleService to be used. 295 | /// * `audit` - The AuditService to be used. 296 | /// 297 | /// # Example 298 | /// 299 | /// ``` 300 | /// let permission_repository = PermissionRepository::new(String::from("permissions")); 301 | /// let permission_service = PermissionService::new(permission_repository); 302 | /// let db = mongodb::Database::new(); 303 | /// let audit_service = AuditService::new(audit_repository); 304 | /// let role_service = RoleService::new(role_repository); 305 | /// let user_id = ObjectId::parse_str("user_id").unwrap(); 306 | /// let id = String::from("id"); 307 | /// 308 | /// permission_service.delete(id, user_id, &db, &role_service, &audit_service); 309 | /// ``` 310 | /// 311 | /// # Returns 312 | /// 313 | /// * `()` - The operation was successful. 314 | /// * `Error` - The Error that occurred. 315 | pub async fn delete( 316 | &self, 317 | id: &str, 318 | user_id: Option, 319 | db: &Database, 320 | role_service: &RoleService, 321 | audit: &AuditService, 322 | ) -> Result<(), Error> { 323 | info!("Deleting Permission by ID: {}", id); 324 | 325 | if user_id.is_some() { 326 | let oid = match ObjectId::parse_str(id) { 327 | Ok(oid) => oid, 328 | Err(e) => { 329 | return Err(Error::Audit(AuditError::ObjectId(e.to_string()))); 330 | } 331 | }; 332 | 333 | let new_audit = Audit::new( 334 | user_id.unwrap(), 335 | Delete, 336 | oid, 337 | ResourceIdType::PermissionId, 338 | PermissionResourceType, 339 | ); 340 | match audit.create(new_audit, db).await { 341 | Ok(_) => {} 342 | Err(e) => { 343 | error!("Failed to create Audit: {}", e); 344 | return Err(Error::Audit(e)); 345 | } 346 | } 347 | } 348 | 349 | self.permission_repository 350 | .delete(id, db, role_service) 351 | .await 352 | } 353 | 354 | /// # Summary 355 | /// 356 | /// Search for Permission entities by text. 357 | /// 358 | /// # Arguments 359 | /// 360 | /// * `text` - The text to search for. 361 | /// * `limit` - The limit of Permission entities to find. 362 | /// * `page` - The page of Permission entities to find. 363 | /// * `db` - The Database to be used. 364 | /// 365 | /// # Example 366 | /// 367 | /// ``` 368 | /// let permission_repository = PermissionRepository::new(String::from("permissions")); 369 | /// let permission_service = PermissionService::new(permission_repository); 370 | /// let db = mongodb::Database::new(); 371 | /// let text = String::from("text"); 372 | /// let permissions = permission_service.search(text, limit, page, &db); 373 | /// ``` 374 | /// 375 | /// # Returns 376 | /// 377 | /// * `Vec` - The Permission entities. 378 | /// * `Error` - The Error that occurred. 379 | pub async fn search( 380 | &self, 381 | text: &str, 382 | limit: Option, 383 | page: Option, 384 | db: &Database, 385 | ) -> Result, Error> { 386 | info!("Searching for Permission by text: {}", text); 387 | self.permission_repository 388 | .search(text, limit, page, db) 389 | .await 390 | } 391 | } 392 | -------------------------------------------------------------------------------- /src/services/role.rs: -------------------------------------------------------------------------------- 1 | pub mod role_service; 2 | -------------------------------------------------------------------------------- /src/services/role/role_service.rs: -------------------------------------------------------------------------------- 1 | use crate::repository::audit::audit_model::Action::{Create, Delete, Update}; 2 | use crate::repository::audit::audit_model::{Audit, ResourceIdType, ResourceType}; 3 | use crate::repository::audit::audit_repository::Error as AuditError; 4 | use crate::repository::role::role_model::Role; 5 | use crate::repository::role::role_repository::{Error, RoleRepository}; 6 | use crate::services::audit::audit_service::AuditService; 7 | use crate::services::user::user_service::UserService; 8 | use log::{error, info}; 9 | use mongodb::bson::oid::ObjectId; 10 | use mongodb::Database; 11 | 12 | #[derive(Clone)] 13 | pub struct RoleService { 14 | pub role_repository: RoleRepository, 15 | } 16 | 17 | impl RoleService { 18 | /// # Summary 19 | /// 20 | /// Create a new RoleService. 21 | /// 22 | /// # Arguments 23 | /// 24 | /// * `role_repository` - The RoleRepository to be used by the RoleService. 25 | /// 26 | /// # Example 27 | /// 28 | /// ``` 29 | /// let role_repository = RoleRepository::new(String::from("roles")); 30 | /// let role_service = RoleService::new(role_repository); 31 | /// ``` 32 | /// 33 | /// # Returns 34 | /// 35 | /// * `RoleService` - The new RoleService. 36 | pub fn new(role_repository: RoleRepository) -> RoleService { 37 | RoleService { role_repository } 38 | } 39 | 40 | /// # Summary 41 | /// 42 | /// Create a new Role. 43 | /// 44 | /// # Arguments 45 | /// 46 | /// * `role` - The Role to be created. 47 | /// * `user_id` - The id of the User creating the Role. 48 | /// * `db` - The Database to be used. 49 | /// * `audit_service` - The AuditService to be used. 50 | /// 51 | /// # Example 52 | /// 53 | /// ``` 54 | /// let role_repository = RoleRepository::new(String::from("roles")); 55 | /// let role_service = RoleService::new(role_repository); 56 | /// let db = mongodb::Database::new(); 57 | /// let audit_service = AuditService::new(AuditRepository::new(String::from("audits"))); 58 | /// let role = Role::new(String::from("role_name")); 59 | /// let user_id = Some(ObjectId::parse_str("user_id")); 60 | /// 61 | /// let role = role_service.create(role, user_id, &db, &audit_service); 62 | /// ``` 63 | /// 64 | /// # Returns 65 | /// 66 | /// * `Role` - The created Role entity. 67 | /// * `Error` - The Error that occurred. 68 | pub async fn create( 69 | &self, 70 | role: Role, 71 | user_id: Option, 72 | db: &Database, 73 | audit_service: &AuditService, 74 | ) -> Result { 75 | info!("Creating Role: {}", role); 76 | 77 | if user_id.is_some() { 78 | let new_audit = Audit::new( 79 | user_id.unwrap(), 80 | Create, 81 | role.id, 82 | ResourceIdType::RoleId, 83 | ResourceType::Role, 84 | ); 85 | match audit_service.create(new_audit, db).await { 86 | Ok(_) => {} 87 | Err(e) => { 88 | error!("Failed to create Audit: {}", e); 89 | return Err(Error::Audit(e)); 90 | } 91 | } 92 | } 93 | 94 | self.role_repository.create(role, db).await 95 | } 96 | 97 | /// # Summary 98 | /// 99 | /// Find all Role entities. 100 | /// 101 | /// # Arguments 102 | /// 103 | /// * `limit` - The limit of Role entities to find. 104 | /// * `page` - The page of Role entities to find. 105 | /// * `db` - The Database to be used. 106 | /// 107 | /// # Example 108 | /// 109 | /// ``` 110 | /// let role_repository = RoleRepository::new(String::from("roles")); 111 | /// let role_service = RoleService::new(role_repository); 112 | /// let db = mongodb::Database::new(); 113 | /// 114 | /// let roles = role_service.find_all(Some(100), Some(1), &db); 115 | /// ``` 116 | /// 117 | /// # Returns 118 | /// 119 | /// * `Vec` - The Role entities. 120 | /// * `Error` - The Error that occurred. 121 | pub async fn find_all( 122 | &self, 123 | limit: Option, 124 | page: Option, 125 | db: &Database, 126 | ) -> Result, Error> { 127 | info!("Finding all roles"); 128 | self.role_repository.find_all(limit, page, db).await 129 | } 130 | 131 | /// # Summary 132 | /// 133 | /// Find a Role entity by its ID. 134 | /// 135 | /// # Arguments 136 | /// 137 | /// * `id` - The id of the Role entity. 138 | /// * `user_id` - The id of the User finding the Role. 139 | /// * `db` - The Database to be used. 140 | /// * `audit_service` - The AuditService to be used. 141 | /// 142 | /// # Example 143 | /// 144 | /// ``` 145 | /// let role_repository = RoleRepository::new(String::from("roles")); 146 | /// let role_service = RoleService::new(role_repository); 147 | /// let db = mongodb::Database::new(); 148 | /// let id = "role_id"; 149 | /// 150 | /// let role = role_service.find_by_id(id, &db); 151 | /// ``` 152 | /// 153 | /// # Returns 154 | /// 155 | /// * `Option` - The optional Role entity. 156 | /// * `Error` - The Error that occurred. 157 | pub async fn find_by_id(&self, id: &str, db: &Database) -> Result, Error> { 158 | info!("Finding Role by ID: {}", id); 159 | self.role_repository.find_by_id(id, db).await 160 | } 161 | 162 | /// # Summary 163 | /// 164 | /// Find a vector of Role entities by their ids. 165 | /// 166 | /// # Arguments 167 | /// 168 | /// * `id_vec` - The vector of ids of the Role entities. 169 | /// * `user_id` - The id of the User finding the Role entities. 170 | /// * `db` - The Database to be used. 171 | /// * `audit_service` - The AuditService to be used. 172 | /// 173 | /// # Example 174 | /// 175 | /// ``` 176 | /// let role_repository = RoleRepository::new(String::from("roles")); 177 | /// let role_service = RoleService::new(role_repository); 178 | /// let db = mongodb::Database::new(); 179 | /// let id_vec = vec!["role_id"]; 180 | /// 181 | /// let roles = role_service.find_by_id_vec(id_vec, &db); 182 | /// ``` 183 | /// 184 | /// # Returns 185 | /// 186 | /// * `Vec` - The vector of Role entities. 187 | /// * `Error` - The Error that occurred. 188 | pub async fn find_by_id_vec( 189 | &self, 190 | id_vec: Vec, 191 | db: &Database, 192 | ) -> Result, Error> { 193 | info!("Finding roles by id vec: {:?}", id_vec); 194 | self.role_repository.find_by_id_vec(id_vec, db).await 195 | } 196 | 197 | /// # Summary 198 | /// 199 | /// Find a role by its name. 200 | /// 201 | /// # Arguments 202 | /// 203 | /// * `name` - A string slice that holds the name. 204 | /// * `user_id` - The id of the User finding the Role. 205 | /// * `db` - A reference to a Database instance. 206 | /// * `audit_service` - A reference to an AuditService instance. 207 | /// 208 | /// # Example 209 | /// 210 | /// ``` 211 | /// let role_repository = RoleRepository::new(String::from("roles")); 212 | /// let role_service = RoleService::new(role_repository); 213 | /// let db = mongodb::Database::new(); 214 | /// let name = "role_name"; 215 | /// 216 | /// let role = role_service.find_by_name(name, &db); 217 | /// ``` 218 | /// 219 | /// # Returns 220 | /// 221 | /// A Result with an Option of a Role instance or an Error. 222 | pub async fn find_by_name(&self, name: &str, db: &Database) -> Result, Error> { 223 | info!("Finding Role by name: {}", name); 224 | self.role_repository.find_by_name(name, db).await 225 | } 226 | 227 | /// # Summary 228 | /// 229 | /// Update a Role entity. 230 | /// 231 | /// # Arguments 232 | /// 233 | /// * `role` - The Role entity. 234 | /// * `user_id` - The id of the User updating the Role entity. 235 | /// * `db` - The Database to be used. 236 | /// * `audit_service` - The AuditService to be used. 237 | /// 238 | /// # Example 239 | /// 240 | /// ``` 241 | /// let role_repository = RoleRepository::new(String::from("roles")); 242 | /// let role_service = RoleService::new(role_repository); 243 | /// let db = mongodb::Database::new(); 244 | /// let audit_service = AuditService::new(AuditRepository::new(String::from("audits"))); 245 | /// let role = Role::new(String::from("role_name"), vec!["permission_id"]); 246 | /// let user_id = Some(ObjectId::parse_str("user_id")); 247 | /// 248 | /// let updated_role = role_service.update(role, user_id, &db, &audit_service); 249 | /// ``` 250 | /// 251 | /// # Returns 252 | /// 253 | /// * `Role` - The updated Role entity. 254 | /// * `Error` - The Error that occurred. 255 | pub async fn update( 256 | &self, 257 | role: Role, 258 | user_id: Option, 259 | db: &Database, 260 | audit_service: &AuditService, 261 | ) -> Result { 262 | info!("Updating Role: {}", role); 263 | 264 | if user_id.is_some() { 265 | let new_audit = Audit::new( 266 | user_id.unwrap(), 267 | Update, 268 | role.id, 269 | ResourceIdType::RoleId, 270 | ResourceType::Role, 271 | ); 272 | match audit_service.create(new_audit, db).await { 273 | Ok(_) => {} 274 | Err(e) => { 275 | error!("Failed to create Audit: {}", e); 276 | return Err(Error::Audit(e)); 277 | } 278 | } 279 | } 280 | 281 | self.role_repository.update(role, db).await 282 | } 283 | 284 | /// # Summary 285 | /// 286 | /// Delete a Role entity by its id. 287 | /// 288 | /// # Arguments 289 | /// 290 | /// * `id` - The id of the Role entity. 291 | /// * `user_id` - The id of the User deleting the Role entity. 292 | /// * `db` - The Database to be used. 293 | /// * `user_service` - The UserService to be used. 294 | /// * `audit_service` - The AuditService to be used. 295 | /// 296 | /// # Example 297 | /// 298 | /// ``` 299 | /// let role_repository = RoleRepository::new(String::from("roles")); 300 | /// let role_service = RoleService::new(role_repository); 301 | /// let db = mongodb::Database::new(); 302 | /// let audit_service = AuditService::new(AuditRepository::new(String::from("audits"))); 303 | /// let id = "role_id"; 304 | /// let user_id = Some(ObjectId::parse_str("user_id")); 305 | /// let user_service = UserService::new(UserRepository::new(String::from("users"))); 306 | /// 307 | /// let res = role_service.delete(id, user_id, &db, &user_service, &audit_service).await; 308 | /// ``` 309 | /// 310 | /// # Returns 311 | /// 312 | /// * `()` - The operation was successful. 313 | /// * `Error` - The Error that occurred. 314 | pub async fn delete( 315 | &self, 316 | id: &str, 317 | user_id: Option, 318 | db: &Database, 319 | user_service: &UserService, 320 | audit_service: &AuditService, 321 | ) -> Result<(), Error> { 322 | info!("Deleting Role by ID: {}", id); 323 | 324 | if user_id.is_some() { 325 | let oid = match ObjectId::parse_str(id) { 326 | Ok(oid) => oid, 327 | Err(e) => { 328 | return Err(Error::Audit(AuditError::ObjectId(e.to_string()))); 329 | } 330 | }; 331 | 332 | let new_audit = Audit::new( 333 | user_id.unwrap(), 334 | Delete, 335 | oid, 336 | ResourceIdType::RoleId, 337 | ResourceType::Role, 338 | ); 339 | match audit_service.create(new_audit, db).await { 340 | Ok(_) => {} 341 | Err(e) => { 342 | error!("Failed to create Audit: {}", e); 343 | return Err(Error::Audit(e)); 344 | } 345 | } 346 | } 347 | 348 | self.role_repository.delete(id, db, user_service).await 349 | } 350 | 351 | /// # Summary 352 | /// 353 | /// Delete a Permission entity from all Role entities. 354 | /// 355 | /// # Arguments 356 | /// 357 | /// * `permission_id` - The id of the Permission entity. 358 | /// * `db` - The Database to be used. 359 | /// 360 | /// # Example 361 | /// 362 | /// ``` 363 | /// let role_repository = RoleRepository::new(String::from("roles")); 364 | /// let role_service = RoleService::new(role_repository); 365 | /// let db = mongodb::Database::new(); 366 | /// 367 | /// let res = role_service.delete_permission_from_all_roles("id", &db).await; 368 | /// ``` 369 | /// 370 | /// # Returns 371 | /// 372 | /// * `()` - The operation was successful. 373 | /// * `Error` - The Error that occurred. 374 | pub async fn delete_permission_from_all_roles( 375 | &self, 376 | permission_id: &str, 377 | db: &Database, 378 | ) -> Result<(), Error> { 379 | info!( 380 | "Deleting permission {} from all Role entities", 381 | permission_id 382 | ); 383 | self.role_repository 384 | .delete_permission_from_all_roles(permission_id, db) 385 | .await 386 | } 387 | 388 | /// # Summary 389 | /// 390 | /// Search for Role entities by text. 391 | /// 392 | /// # Arguments 393 | /// 394 | /// * `text` - The text to search for. 395 | /// * `limit` - The limit of Role entities to find. 396 | /// * `page` - The page of Role entities to find. 397 | /// * `db` - The Database to be used. 398 | /// 399 | /// # Example 400 | /// 401 | /// ``` 402 | /// let role_repository = RoleRepository::new(String::from("roles")); 403 | /// let role_service = RoleService::new(role_repository); 404 | /// let db = mongodb::Database::new(); 405 | /// 406 | /// let result = role_service.search("text", Some(100), Some(1), &db).await; 407 | /// ``` 408 | /// 409 | /// # Returns 410 | /// 411 | /// * `Vec` - The vector of Role entities. 412 | /// * `Error` - The Error that occurred. 413 | pub async fn search( 414 | &self, 415 | text: &str, 416 | limit: Option, 417 | page: Option, 418 | db: &Database, 419 | ) -> Result, Error> { 420 | info!("Searching for Role by text: {}", text); 421 | self.role_repository.search(text, limit, page, db).await 422 | } 423 | } 424 | -------------------------------------------------------------------------------- /src/services/user.rs: -------------------------------------------------------------------------------- 1 | pub mod user_service; 2 | -------------------------------------------------------------------------------- /src/services/user/user_service.rs: -------------------------------------------------------------------------------- 1 | use crate::repository::audit::audit_model::Action::{Create, Delete, Update}; 2 | use crate::repository::audit::audit_model::{Audit, ResourceIdType, ResourceType}; 3 | use crate::repository::audit::audit_repository::Error as AuditError; 4 | use crate::repository::user::user_model::User; 5 | use crate::repository::user::user_repository::{Error, UserRepository}; 6 | use crate::services::audit::audit_service::AuditService; 7 | use log::{error, info}; 8 | use mongodb::bson::oid::ObjectId; 9 | use mongodb::Database; 10 | 11 | #[derive(Clone)] 12 | pub struct UserService { 13 | pub user_repository: UserRepository, 14 | } 15 | 16 | impl UserService { 17 | /// # Summary 18 | /// 19 | /// Create a new UserService. 20 | /// 21 | /// # Arguments 22 | /// 23 | /// * `user_repository` - The UserRepository to be used by the UserService. 24 | /// 25 | /// # Example 26 | /// 27 | /// ``` 28 | /// let user_repository = UserRepository::new(String::from("users")); 29 | /// let user_service = UserService::new(user_repository); 30 | /// ``` 31 | /// 32 | /// # Returns 33 | /// 34 | /// * `UserService` - The new UserService. 35 | pub fn new(user_repository: UserRepository) -> UserService { 36 | UserService { user_repository } 37 | } 38 | 39 | /// # Summary 40 | /// 41 | /// Create a User entity. 42 | /// 43 | /// # Arguments 44 | /// 45 | /// * `user` - The User entity to be created. 46 | /// * `user_id` - The ID of the User entity that is creating the new User. 47 | /// * `db` - The Database to be used. 48 | /// * `audit_service` - The AuditService to be used. 49 | /// 50 | /// # Example 51 | /// 52 | /// ``` 53 | /// let user_repository = UserRepository::new(String::from("users")); 54 | /// let user_service = UserService::new(user_repository); 55 | /// let db = mongodb::Database::new(); 56 | /// let audit_service = AuditService::new(AuditRepository::new(String::from("audits"))); 57 | /// let user = User::new("username", "password"); 58 | /// 59 | /// let user = user_service.create(user, ObjectId::parse_str("user_id").unwrap(), &db, &audit_service); 60 | /// ``` 61 | /// 62 | /// # Returns 63 | /// 64 | /// * `User` - The created User entity. 65 | /// * `Error` - The Error that occurred. 66 | pub async fn create( 67 | &self, 68 | user: User, 69 | user_id: Option, 70 | db: &Database, 71 | audit_service: &AuditService, 72 | ) -> Result { 73 | info!("Creating User: {}", user); 74 | 75 | if user_id.is_some() { 76 | let new_audit = Audit::new( 77 | user_id.unwrap(), 78 | Create, 79 | user.id, 80 | ResourceIdType::UserId, 81 | ResourceType::User, 82 | ); 83 | match audit_service.create(new_audit, db).await { 84 | Ok(_) => {} 85 | Err(e) => { 86 | error!("Failed to create Audit: {}", e); 87 | return Err(Error::Audit(e)); 88 | } 89 | } 90 | } 91 | 92 | self.user_repository.create(user, db).await 93 | } 94 | 95 | /// # Summary 96 | /// 97 | /// Find all User entities. 98 | /// 99 | /// # Arguments 100 | /// 101 | /// * `limit` - The maximum number of Users to return. 102 | /// * `page` - The page of Users to return. 103 | /// * `db` - The Database to be used. 104 | /// 105 | /// # Example 106 | /// 107 | /// ``` 108 | /// let user_repository = UserRepository::new(String::from("users")); 109 | /// let user_service = UserService::new(user_repository); 110 | /// let db = mongodb::Database::new(); 111 | /// let users = user_service.find_all(Some(10), Some(1), &db); 112 | /// ``` 113 | /// 114 | /// # Returns 115 | /// 116 | /// * `Vec` - The found User entities. 117 | /// * `Error` - The Error that occurred. 118 | pub async fn find_all( 119 | &self, 120 | limit: Option, 121 | page: Option, 122 | db: &Database, 123 | ) -> Result, Error> { 124 | info!("Finding all users"); 125 | self.user_repository.find_all(limit, page, db).await 126 | } 127 | 128 | /// # Summary 129 | /// 130 | /// Find a User entity by ID. 131 | /// 132 | /// # Arguments 133 | /// 134 | /// * `id` - The ID of the User entity. 135 | /// * `db` - The Database to be used. 136 | /// 137 | /// # Example 138 | /// 139 | /// ``` 140 | /// let user_repository = UserRepository::new(String::from("users")); 141 | /// let user_service = UserService::new(user_repository); 142 | /// let db = mongodb::Database::new(); 143 | /// 144 | /// let user = user_service.find_by_id("id", &db); 145 | /// ``` 146 | /// 147 | /// # Returns 148 | /// 149 | /// * `Option` - The created User entity. 150 | /// * `Error` - The Error that occurred. 151 | pub async fn find_by_id(&self, id: &str, db: &Database) -> Result, Error> { 152 | info!("Finding User by ID: {}", id); 153 | self.user_repository.find_by_id(id, db).await 154 | } 155 | 156 | /// # Summary 157 | /// 158 | /// Find a User entity by its username. 159 | /// 160 | /// # Arguments 161 | /// 162 | /// * `username` - The username of the User entity. 163 | /// * `user_id` - The ID of the User entity that is finding the User. 164 | /// * `db` - The Database. 165 | /// * `audit_service` - The AuditService. 166 | /// 167 | /// # Example 168 | /// 169 | /// ``` 170 | /// let user_repository = UserRepository::new(String::from("users")); 171 | /// let user_service = UserService::new(user_repository); 172 | /// let db = mongodb::Database::new(); 173 | /// let user = user_service.find_by_username("username", &db); 174 | /// ``` 175 | /// 176 | /// # Returns 177 | /// 178 | /// * `Result, Error>` - The result of the operation. 179 | pub async fn find_by_username( 180 | &self, 181 | username: &str, 182 | db: &Database, 183 | ) -> Result, Error> { 184 | info!("Finding User by username: {}", username); 185 | self.user_repository.find_by_username(username, db).await 186 | } 187 | 188 | /// # Summary 189 | /// 190 | /// Update a user entity. 191 | /// 192 | /// # Arguments 193 | /// 194 | /// * `user` - The User entity to be updated including its updated values. 195 | /// * `user_id` - The ID of the User entity that is updating the User. 196 | /// * `db` - The Database to be used. 197 | /// * `audit_service` - The AuditService to be used. 198 | /// 199 | /// # Example 200 | /// 201 | /// ``` 202 | /// let user_repository = UserRepository::new(String::from("users")); 203 | /// let user_service = UserService::new(user_repository); 204 | /// let db = mongodb::Database::new(); 205 | /// let audit_service = AuditService::new(AuditRepository::new(String::from("audits"))); 206 | /// 207 | /// let user = user_service.update(User::new(), ObjectId::parse_str("id").unwrap(), &db); 208 | /// ``` 209 | /// 210 | /// # Returns 211 | /// 212 | /// * `User` - The updated User entity. 213 | /// * `Error` - The Error that occurred. 214 | pub async fn update( 215 | &self, 216 | user: User, 217 | user_id: Option, 218 | db: &Database, 219 | audit_service: &AuditService, 220 | ) -> Result { 221 | info!("Updating User: {}", user); 222 | 223 | if user_id.is_some() { 224 | let new_audit = Audit::new( 225 | user_id.unwrap(), 226 | Update, 227 | user.id, 228 | ResourceIdType::UserId, 229 | ResourceType::User, 230 | ); 231 | match audit_service.create(new_audit, db).await { 232 | Ok(_) => {} 233 | Err(e) => { 234 | error!("Failed to create Audit: {}", e); 235 | return Err(Error::Audit(e)); 236 | } 237 | } 238 | } 239 | 240 | self.user_repository.update(user, db).await 241 | } 242 | 243 | /// # Summary 244 | /// 245 | /// Update a User entity's password. 246 | /// 247 | /// # Arguments 248 | /// 249 | /// * `id` - The ID of the User entity to be updated. 250 | /// * `password` - The new password of the User entity. 251 | /// * `user_id` - The ID of the User entity that is updating the User. 252 | /// * `db` - The Database to be used. 253 | /// * `audit_service` - The AuditService to be used. 254 | /// 255 | /// # Example 256 | /// 257 | /// ``` 258 | /// let user_repository = UserRepository::new(String::from("users")); 259 | /// let user_service = UserService::new(user_repository); 260 | /// let db = mongodb::Database::new(); 261 | /// let audit_service = AuditService::new(AuditRepository::new(String::from("audits"))); 262 | /// 263 | /// let user = user_service.update_password("id", "password", ObjectId::parse_str("user_id").unwrap(), &db); 264 | /// ``` 265 | /// 266 | /// # Returns 267 | /// 268 | /// * `()` - The update operation was successful. 269 | /// * `Error` - The Error that occurred. 270 | pub async fn update_password( 271 | &self, 272 | id: &str, 273 | password: &str, 274 | user_id: Option, 275 | db: &Database, 276 | audit_service: &AuditService, 277 | ) -> Result<(), Error> { 278 | info!("Updating User password: {}", id); 279 | 280 | if user_id.is_some() { 281 | let oid = match ObjectId::parse_str(id) { 282 | Ok(oid) => oid, 283 | Err(e) => { 284 | return Err(Error::Audit(AuditError::ObjectId(e.to_string()))); 285 | } 286 | }; 287 | 288 | let new_audit = Audit::new( 289 | user_id.unwrap(), 290 | Update, 291 | oid, 292 | ResourceIdType::UserId, 293 | ResourceType::User, 294 | ); 295 | match audit_service.create(new_audit, db).await { 296 | Ok(_) => {} 297 | Err(e) => { 298 | error!("Failed to create Audit: {}", e); 299 | return Err(Error::Audit(e)); 300 | } 301 | } 302 | } 303 | 304 | self.user_repository.update_password(id, password, db).await 305 | } 306 | 307 | /// # Summary 308 | /// 309 | /// Delete a User entity by ID. 310 | /// 311 | /// # Arguments 312 | /// 313 | /// * `id` - The ID of the User entity to be deleted. 314 | /// * `user_id` - The ID of the User entity that is deleting the User. 315 | /// * `db` - The Database to be used. 316 | /// * `audit_service` - The AuditService to be used. 317 | /// 318 | /// # Example 319 | /// 320 | /// ``` 321 | /// let user_repository = UserRepository::new(String::from("users")); 322 | /// let user_service = UserService::new(user_repository); 323 | /// let db = mongodb::Database::new(); 324 | /// let audit_service = AuditService::new(AuditRepository::new(String::from("audits"))); 325 | /// 326 | /// user_service.delete("id", ObjectId::parse_str("user_id").unwrap(), &db); 327 | /// ``` 328 | /// 329 | /// # Returns 330 | /// 331 | /// * `()` - The delete operation was successful. 332 | /// * `Error` - The Error that occurred. 333 | pub async fn delete( 334 | &self, 335 | id: &str, 336 | user_id: Option, 337 | db: &Database, 338 | audit_service: &AuditService, 339 | ) -> Result<(), Error> { 340 | info!("Deleting User: {}", id); 341 | 342 | if user_id.is_some() { 343 | let oid = match ObjectId::parse_str(id) { 344 | Ok(oid) => oid, 345 | Err(e) => { 346 | return Err(Error::Audit(AuditError::ObjectId(e.to_string()))); 347 | } 348 | }; 349 | 350 | let new_audit = Audit::new( 351 | user_id.unwrap(), 352 | Delete, 353 | oid, 354 | ResourceIdType::UserId, 355 | ResourceType::User, 356 | ); 357 | match audit_service.create(new_audit, db).await { 358 | Ok(_) => {} 359 | Err(e) => { 360 | error!("Failed to create Audit: {}", e); 361 | return Err(Error::Audit(e)); 362 | } 363 | } 364 | } 365 | 366 | self.user_repository.delete(id, db).await 367 | } 368 | 369 | /// # Summary 370 | /// 371 | /// Delete a Role from all Users. 372 | /// 373 | /// # Arguments 374 | /// 375 | /// * `role_id` - The ID of the Role entity to be deleted from all Users. 376 | /// * `db` - The Database to be used. 377 | /// 378 | /// # Example 379 | /// 380 | /// ``` 381 | /// let user_repository = UserRepository::new(String::from("users")); 382 | /// let user_service = UserService::new(user_repository); 383 | /// let db = mongodb::Database::new(); 384 | /// 385 | /// user_service.delete_role_from_all_users("role_id", &db); 386 | /// ``` 387 | /// 388 | /// # Returns 389 | /// 390 | /// * `()` - The delete operation was successful. 391 | /// * `Error` - The Error that occurred. 392 | pub async fn delete_role_from_all_users( 393 | &self, 394 | role_id: &str, 395 | db: &Database, 396 | ) -> Result<(), Error> { 397 | info!("Deleting Role from all Users: {}", role_id); 398 | self.user_repository 399 | .delete_role_from_all_users(role_id, db) 400 | .await 401 | } 402 | 403 | /// # Summary 404 | /// 405 | /// Search for Users. 406 | /// 407 | /// # Arguments 408 | /// 409 | /// * `text` - The text to search for. 410 | /// * `limit` - The maximum number of Users to return. 411 | /// * `page` - The page of Users to return. 412 | /// * `db` - The Database to be used. 413 | /// 414 | /// # Example 415 | /// 416 | /// ``` 417 | /// let user_repository = UserRepository::new(String::from("users")); 418 | /// let user_service = UserService::new(user_repository); 419 | /// let db = mongodb::Database::new(); 420 | /// let users = user_service.search("text", Some(10), Some(1), &db); 421 | /// ``` 422 | /// 423 | /// # Returns 424 | /// 425 | /// * `Vec` - The Users that match the search criteria. 426 | /// * `Error` - The Error that occurred. 427 | pub async fn search( 428 | &self, 429 | text: &str, 430 | limit: Option, 431 | page: Option, 432 | db: &Database, 433 | ) -> Result, Error> { 434 | info!("Searching Users: {}", text); 435 | self.user_repository.search(text, limit, page, db).await 436 | } 437 | } 438 | -------------------------------------------------------------------------------- /src/web.rs: -------------------------------------------------------------------------------- 1 | pub mod controller; 2 | pub mod dto; 3 | pub mod extractors; 4 | -------------------------------------------------------------------------------- /src/web/controller.rs: -------------------------------------------------------------------------------- 1 | use crate::web::controller::authentication::authentication_controller; 2 | use crate::web::controller::health::health_controller; 3 | use crate::web::controller::permission::permission_controller; 4 | use crate::web::controller::role::role_controller; 5 | use crate::web::controller::user::user_controller; 6 | use actix_web::web; 7 | 8 | pub mod audit; 9 | pub mod authentication; 10 | pub mod health; 11 | pub mod permission; 12 | pub mod role; 13 | pub mod user; 14 | 15 | pub struct Controller {} 16 | 17 | impl Controller { 18 | /// # Summary 19 | /// 20 | /// Configure the routes for the web server. 21 | /// 22 | /// # Arguments 23 | /// 24 | /// * `cfg` - The web server configuration. 25 | pub fn configure_routes(cfg: &mut web::ServiceConfig) { 26 | cfg.service( 27 | web::scope("/api/v1") 28 | .service( 29 | web::scope("/permissions") 30 | .service(permission_controller::create_permission) 31 | .service(permission_controller::find_all_permissions) 32 | .service(permission_controller::find_by_id) 33 | .service(permission_controller::update_permission) 34 | .service(permission_controller::delete_permission), 35 | ) 36 | .service( 37 | web::scope("/roles") 38 | .service(role_controller::create) 39 | .service(role_controller::find_all_roles) 40 | .service(role_controller::find_by_id) 41 | .service(role_controller::update) 42 | .service(role_controller::delete), 43 | ) 44 | .service( 45 | web::scope("/users") 46 | .service(user_controller::create) 47 | .service(user_controller::find_all) 48 | .service(user_controller::find_by_id) 49 | .service(user_controller::update) 50 | .service(user_controller::update_password) 51 | .service(user_controller::admin_update_password) 52 | .service(user_controller::delete) 53 | .service(user_controller::delete_self), 54 | ) 55 | .service( 56 | web::scope("/authentication") 57 | .service(authentication_controller::login) 58 | .service(authentication_controller::current_user) 59 | .service(authentication_controller::register), 60 | ) 61 | .service( 62 | web::scope("/audits") 63 | .service(audit::audit_controller::find_all) 64 | .service(audit::audit_controller::find_by_id), 65 | ), 66 | ); 67 | 68 | cfg.service(web::scope("/health").service(health_controller::health)); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/web/controller/audit.rs: -------------------------------------------------------------------------------- 1 | pub mod audit_controller; 2 | -------------------------------------------------------------------------------- /src/web/controller/audit/audit_controller.rs: -------------------------------------------------------------------------------- 1 | use crate::configuration::config::Config; 2 | use crate::errors::internal_server_error::InternalServerError; 3 | use crate::web::dto::audit::audit_dto::AuditDto; 4 | use crate::web::dto::search::search_request::SearchRequest; 5 | use actix_web::{get, web, HttpResponse}; 6 | use actix_web_grants::protect; 7 | use log::error; 8 | 9 | #[utoipa::path( 10 | get, 11 | path = "/api/v1/audits/", 12 | params( 13 | ("text" = Option, Query, description = "The text to search for", nullable = true), 14 | ("limit" = Option, Query, description = "The limit of audits to retrieve", nullable = true), 15 | ("page" = Option, Query, description = "The page", nullable = true), 16 | ), 17 | responses( 18 | (status = 200, description = "OK", body = Vec), 19 | (status = 204, description = "No Content"), 20 | (status = 500, description = "Internal Server Error", body = InternalServerError), 21 | ), 22 | tag = "Audits", 23 | security( 24 | ("Token" = []) 25 | ) 26 | )] 27 | #[get("/")] 28 | #[protect("CAN_READ_AUDIT")] 29 | pub async fn find_all(search: web::Query, pool: web::Data) -> HttpResponse { 30 | let search = search.into_inner(); 31 | 32 | let mut limit = search.limit; 33 | let page = search.page; 34 | 35 | let limit_clone = limit.unwrap_or(pool.server_config.max_limit); 36 | if limit.is_none() 37 | || (limit.is_some() && limit_clone > pool.server_config.max_limit || limit_clone < 1) 38 | { 39 | limit = Some(pool.server_config.max_limit); 40 | } 41 | 42 | let res = match search.text { 43 | Some(t) => match pool 44 | .services 45 | .audit_service 46 | .search(&t, limit, page, &pool.database) 47 | .await 48 | { 49 | Ok(d) => d, 50 | Err(e) => { 51 | error!("Error while searching for audits: {}", e); 52 | return HttpResponse::InternalServerError() 53 | .json(InternalServerError::new(&e.to_string())); 54 | } 55 | }, 56 | None => match pool 57 | .services 58 | .audit_service 59 | .find_all(limit, page, &pool.database) 60 | .await 61 | { 62 | Ok(d) => d, 63 | Err(e) => { 64 | error!("Error while finding all audits: {}", e); 65 | return HttpResponse::InternalServerError() 66 | .json(InternalServerError::new(&e.to_string())); 67 | } 68 | }, 69 | }; 70 | 71 | if res.is_empty() { 72 | return HttpResponse::NoContent().finish(); 73 | } 74 | 75 | let dto_list = res.into_iter().map(|p| p.into()).collect::>(); 76 | 77 | HttpResponse::Ok().json(dto_list) 78 | } 79 | 80 | #[utoipa::path( 81 | get, 82 | path = "/api/v1/audits/{id}", 83 | params( 84 | ("id" = String, Path, description = "The ID of the Audit"), 85 | ), 86 | responses( 87 | (status = 200, description = "OK", body = AuditDto), 88 | (status = 404, description = "Not Found"), 89 | (status = 500, description = "Internal Server Error", body = InternalServerError), 90 | ), 91 | tag = "Audits", 92 | security( 93 | ("Token" = []) 94 | ) 95 | )] 96 | #[get("/{id}")] 97 | #[protect("CAN_READ_AUDIT")] 98 | pub async fn find_by_id(path: web::Path, pool: web::Data) -> HttpResponse { 99 | let res = match pool 100 | .services 101 | .audit_service 102 | .find_by_id(&path, &pool.database) 103 | .await 104 | { 105 | Ok(d) => match d { 106 | Some(d) => d, 107 | None => return HttpResponse::NotFound().finish(), 108 | }, 109 | Err(e) => { 110 | error!("Error while finding Audit with ID {}: {}", path, e); 111 | return HttpResponse::InternalServerError() 112 | .json(InternalServerError::new(&e.to_string())); 113 | } 114 | }; 115 | 116 | HttpResponse::Ok().json(AuditDto::from(res)) 117 | } 118 | -------------------------------------------------------------------------------- /src/web/controller/authentication.rs: -------------------------------------------------------------------------------- 1 | pub mod authentication_controller; 2 | -------------------------------------------------------------------------------- /src/web/controller/authentication/authentication_controller.rs: -------------------------------------------------------------------------------- 1 | use crate::configuration::config::Config; 2 | use crate::errors::bad_request::BadRequest; 3 | use crate::errors::internal_server_error::InternalServerError; 4 | use crate::repository::user::user_model::User; 5 | use crate::repository::user::user_repository::Error; 6 | use crate::services::password::password_service::PasswordService; 7 | use crate::web::controller::user::user_controller::ConvertError; 8 | use crate::web::dto::authentication::login_request::LoginRequest; 9 | use crate::web::dto::authentication::login_response::LoginResponse; 10 | use crate::web::dto::authentication::register_request::RegisterRequest; 11 | use crate::web::dto::permission::permission_dto::SimplePermissionDto; 12 | use crate::web::dto::role::role_dto::SimpleRoleDto; 13 | use crate::web::dto::user::user_dto::SimpleUserDto; 14 | use actix_web::{get, post, web, HttpRequest, HttpResponse}; 15 | use argon2::PasswordHash; 16 | use log::error; 17 | use mongodb::bson::oid::ObjectId; 18 | 19 | /// # Summary 20 | /// 21 | /// Convert a User into a SimpleUserDto 22 | /// 23 | /// # Arguments 24 | /// 25 | /// * `user` - A User 26 | /// * `pool` - The database connection pool 27 | /// * `audit_service` - The AuditService 28 | /// 29 | /// # Example 30 | /// 31 | /// ``` 32 | /// let user_dto = convert_user_to_simple_dto(user, &pool, &audit_service).await; 33 | /// ``` 34 | /// 35 | /// # Returns 36 | /// 37 | /// * `Result` - The result containing the SimpleUserDto or the ConvertError that occurred 38 | pub async fn convert_user_to_simple_dto( 39 | user: User, 40 | pool: &Config, 41 | ) -> Result { 42 | let mut user_dto = SimpleUserDto::from(user.clone()); 43 | 44 | if user.roles.is_some() { 45 | let mut role_vec: Vec = vec![]; 46 | for r in user.roles.unwrap() { 47 | role_vec.push(r.to_hex()); 48 | } 49 | 50 | let roles = match pool 51 | .services 52 | .role_service 53 | .find_by_id_vec(role_vec, &pool.database) 54 | .await 55 | { 56 | Ok(d) => d, 57 | Err(e) => { 58 | return Err(ConvertError::RoleError(e)); 59 | } 60 | }; 61 | 62 | if !roles.is_empty() { 63 | let mut role_dto_list: Vec = vec![]; 64 | 65 | for r in &roles { 66 | let mut role_dto = SimpleRoleDto::from(r); 67 | if r.permissions.is_some() { 68 | let mut permission_dto_list: Vec = vec![]; 69 | 70 | let mut p_id_vec: Vec = vec![]; 71 | for p in r.permissions.clone().unwrap() { 72 | p_id_vec.push(p.to_hex()); 73 | } 74 | 75 | let permissions = match pool 76 | .services 77 | .permission_service 78 | .find_by_id_vec(p_id_vec, &pool.database) 79 | .await 80 | { 81 | Ok(d) => d, 82 | Err(e) => return Err(ConvertError::PermissionError(e)), 83 | }; 84 | 85 | if !permissions.is_empty() { 86 | for p in permissions { 87 | permission_dto_list.push(SimplePermissionDto::from(p)); 88 | } 89 | } 90 | 91 | if !permission_dto_list.is_empty() { 92 | role_dto.permissions = Some(permission_dto_list) 93 | } 94 | } 95 | 96 | role_dto_list.push(role_dto); 97 | } 98 | 99 | user_dto.roles = Some(role_dto_list); 100 | } 101 | } 102 | 103 | Ok(user_dto) 104 | } 105 | 106 | #[utoipa::path( 107 | post, 108 | path = "/api/v1/authentication/login/", 109 | request_body = LoginRequest, 110 | responses( 111 | (status = 200, description = "OK", body = LoginResponse), 112 | (status = 400, description = "Bad Request", body = BadRequest), 113 | (status = 500, description = "Internal Server Error", body = InternalServerError), 114 | ), 115 | tag = "Authentication", 116 | )] 117 | #[post("/login/")] 118 | pub async fn login( 119 | login_request: web::Json, 120 | pool: web::Data, 121 | ) -> HttpResponse { 122 | let login_request = login_request.into_inner(); 123 | 124 | if login_request.username.is_empty() { 125 | return HttpResponse::BadRequest().json("Username is required"); 126 | } 127 | if login_request.password.is_empty() { 128 | return HttpResponse::BadRequest().json("Password is required"); 129 | } 130 | 131 | let user = match pool 132 | .services 133 | .user_service 134 | .find_by_username(&login_request.username, &pool.database) 135 | .await 136 | { 137 | Ok(u) => match u { 138 | Some(user) => user, 139 | None => { 140 | return HttpResponse::BadRequest().finish(); 141 | } 142 | }, 143 | Err(e) => { 144 | error!("Failed to find user by email: {}", e); 145 | return HttpResponse::BadRequest().finish(); 146 | } 147 | }; 148 | 149 | let parsed_hash = match PasswordHash::new(&user.password) { 150 | Ok(h) => h, 151 | Err(e) => { 152 | error!("Failed to parse password hash: {}", e); 153 | return HttpResponse::InternalServerError() 154 | .json(InternalServerError::new("Failed to parse password hash")); 155 | } 156 | }; 157 | 158 | if !PasswordService::verify_password(&login_request.password, &parsed_hash) { 159 | return HttpResponse::BadRequest().finish(); 160 | } 161 | 162 | match pool 163 | .services 164 | .jwt_service 165 | .generate_jwt_token(&user.id.to_hex()) 166 | { 167 | Some(t) => HttpResponse::Ok().json(LoginResponse::new(t)), 168 | None => HttpResponse::InternalServerError() 169 | .json(InternalServerError::new("Failed to generate JWT token")), 170 | } 171 | } 172 | 173 | #[utoipa::path( 174 | post, 175 | path = "/api/v1/authentication/register/", 176 | request_body = RegisterRequest, 177 | responses( 178 | (status = 200, description = "OK"), 179 | (status = 400, description = "Bad Request", body = BadRequest), 180 | (status = 500, description = "Internal Server Error", body = InternalServerError), 181 | ), 182 | tag = "Authentication", 183 | )] 184 | #[post("/register/")] 185 | pub async fn register( 186 | register_request: web::Json, 187 | pool: web::Data, 188 | ) -> HttpResponse { 189 | let register_request = register_request.into_inner(); 190 | 191 | if register_request.username.is_empty() { 192 | return HttpResponse::BadRequest().json(BadRequest::new("Empty usernames are not allowed")); 193 | } 194 | 195 | if register_request.password.is_empty() { 196 | return HttpResponse::BadRequest().json(BadRequest::new("Empty passwords are not allowed")); 197 | } 198 | 199 | let default_roles: Option> = match pool 200 | .services 201 | .role_service 202 | .find_by_name("DEFAULT", &pool.database) 203 | .await 204 | { 205 | Ok(r) => match r { 206 | Some(role) => Some(vec![role.id]), 207 | None => None, 208 | }, 209 | Err(e) => { 210 | error!("Failed to find default role: {}", e); 211 | return HttpResponse::InternalServerError() 212 | .json(InternalServerError::new(&e.to_string())); 213 | } 214 | }; 215 | 216 | let mut user = User::from(register_request); 217 | 218 | let password_hash = match PasswordService::hash_password(user.password) { 219 | Ok(e) => e.to_string(), 220 | Err(e) => { 221 | error!("Failed to hash password: {}", e); 222 | return HttpResponse::InternalServerError() 223 | .json(InternalServerError::new("Failed to hash password")); 224 | } 225 | }; 226 | 227 | user.password = password_hash; 228 | user.roles = default_roles; 229 | 230 | let user_id = user.id.clone(); 231 | 232 | match pool 233 | .services 234 | .user_service 235 | .create( 236 | user, 237 | Some(user_id), 238 | &pool.database, 239 | &pool.services.audit_service, 240 | ) 241 | .await 242 | { 243 | Ok(_) => HttpResponse::Ok().finish(), 244 | Err(e) => { 245 | error!("Error creating User: {}", e); 246 | match e { 247 | Error::UsernameAlreadyTaken | Error::EmailAlreadyTaken | Error::InvalidEmail(_) => { 248 | HttpResponse::BadRequest().json(BadRequest::new(&e.to_string())) 249 | } 250 | _ => HttpResponse::InternalServerError() 251 | .json(InternalServerError::new(&e.to_string())), 252 | } 253 | } 254 | } 255 | } 256 | 257 | #[utoipa::path( 258 | get, 259 | path = "/api/v1/authentication/current/", 260 | responses( 261 | (status = 200, description = "OK", body = SimpleUserDto), 262 | (status = 400, description = "Bad Request", body = BadRequest), 263 | (status = 500, description = "Internal Server Error", body = InternalServerError), 264 | ), 265 | tag = "Authentication", 266 | security( 267 | ("Token" = []) 268 | ) 269 | )] 270 | #[get("/current/")] 271 | pub async fn current_user(req: HttpRequest, pool: web::Data) -> HttpResponse { 272 | if let Some(auth_header) = req.headers().get("Authorization") { 273 | if let Ok(auth_str) = auth_header.to_str() { 274 | if let Some(token) = auth_str.strip_prefix("Bearer ") { 275 | let username = match pool.services.jwt_service.verify_jwt_token(token) { 276 | Ok(user) => user, 277 | Err(e) => { 278 | error!("Failed to verify JWT token: {}", e); 279 | return HttpResponse::Forbidden().finish(); 280 | } 281 | }; 282 | 283 | let user = match pool 284 | .services 285 | .user_service 286 | .find_by_id(&username, &pool.database) 287 | .await 288 | { 289 | Ok(u) => match u { 290 | Some(user) => user, 291 | None => { 292 | return HttpResponse::Forbidden().finish(); 293 | } 294 | }, 295 | Err(e) => { 296 | error!("Failed to find user by ID: {}", e); 297 | return HttpResponse::Forbidden().finish(); 298 | } 299 | }; 300 | 301 | if !user.enabled { 302 | return HttpResponse::Forbidden().finish(); 303 | } 304 | 305 | return match convert_user_to_simple_dto(user, &pool).await { 306 | Ok(u) => HttpResponse::Ok().json(u), 307 | Err(e) => { 308 | error!("Failed to convert User to SimpleUserDto: {}", e); 309 | HttpResponse::Forbidden().finish() 310 | } 311 | }; 312 | } 313 | } 314 | } 315 | 316 | HttpResponse::Forbidden().finish() 317 | } 318 | -------------------------------------------------------------------------------- /src/web/controller/health.rs: -------------------------------------------------------------------------------- 1 | pub mod health_controller; 2 | -------------------------------------------------------------------------------- /src/web/controller/health/health_controller.rs: -------------------------------------------------------------------------------- 1 | use actix_web::{get, HttpResponse}; 2 | use serde::{Deserialize, Serialize}; 3 | use utoipa::ToSchema; 4 | 5 | #[derive(Serialize, Deserialize, ToSchema)] 6 | pub struct HealthResponse { 7 | pub status: String, 8 | } 9 | 10 | impl HealthResponse { 11 | /// # Summary 12 | /// 13 | /// Create a new HealthResponse. 14 | /// 15 | /// # Arguments 16 | /// 17 | /// * `status` - The status of the application. 18 | /// 19 | /// # Returns 20 | /// 21 | /// * `HealthResponse` - The new HealthResponse. 22 | pub fn new(status: &str) -> Self { 23 | Self { 24 | status: status.to_string(), 25 | } 26 | } 27 | } 28 | 29 | #[utoipa::path( 30 | get, 31 | path = "/health/", 32 | responses( 33 | (status = 200, description = "OK", body = HealthResponse), 34 | ), 35 | tag = "Health", 36 | )] 37 | #[get("/")] 38 | pub async fn health() -> HttpResponse { 39 | HttpResponse::Ok().json(HealthResponse::new("UP")) 40 | } 41 | -------------------------------------------------------------------------------- /src/web/controller/permission.rs: -------------------------------------------------------------------------------- 1 | pub mod permission_controller; 2 | -------------------------------------------------------------------------------- /src/web/controller/permission/permission_controller.rs: -------------------------------------------------------------------------------- 1 | use crate::configuration::config::Config; 2 | use crate::errors::bad_request::BadRequest; 3 | use crate::errors::internal_server_error::InternalServerError; 4 | use crate::repository::permission::permission_model::Permission; 5 | use crate::repository::permission::permission_repository::Error; 6 | use crate::web::dto::permission::create_permission::CreatePermission; 7 | use crate::web::dto::permission::permission_dto::PermissionDto; 8 | use crate::web::dto::permission::update_permission::UpdatePermission; 9 | use crate::web::dto::search::search_request::SearchRequest; 10 | use crate::web::extractors::user_id_extractor; 11 | use actix_web::{delete, get, post, put, web, HttpRequest, HttpResponse}; 12 | use actix_web_grants::protect; 13 | use log::error; 14 | 15 | #[utoipa::path( 16 | post, 17 | path = "/api/v1/permissions/", 18 | request_body = CreatePermission, 19 | responses( 20 | (status = 200, description = "OK", body = PermissionDto), 21 | (status = 400, description = "Bad Request", body = BadRequest), 22 | (status = 500, description = "Internal Server Error", body = InternalServerError), 23 | ), 24 | tag = "Permissions", 25 | security( 26 | ("Token" = []) 27 | ) 28 | )] 29 | #[post("/")] 30 | #[protect("CAN_CREATE_PERMISSION")] 31 | pub async fn create_permission( 32 | pool: web::Data, 33 | info: web::Json, 34 | req: HttpRequest, 35 | ) -> HttpResponse { 36 | if info.name.is_empty() { 37 | return HttpResponse::BadRequest().json(BadRequest::new("Empty name")); 38 | } 39 | 40 | let new_permission = Permission::from(info.into_inner()); 41 | 42 | let user_id = match user_id_extractor::get_user_id_from_token(&req, &pool).await { 43 | Some(e) => e, 44 | None => { 45 | error!("Failed to get User ID from token"); 46 | return HttpResponse::InternalServerError() 47 | .json(InternalServerError::new("Failed to get User ID from token")); 48 | } 49 | }; 50 | 51 | let res = match pool 52 | .services 53 | .permission_service 54 | .create( 55 | new_permission, 56 | Some(user_id), 57 | &pool.database, 58 | &pool.services.audit_service, 59 | ) 60 | .await 61 | { 62 | Ok(d) => d, 63 | Err(e) => { 64 | error!("Error while creating Permission: {}", e); 65 | return match e { 66 | Error::NameAlreadyTaken => { 67 | HttpResponse::BadRequest().json(BadRequest::new(&e.to_string())) 68 | } 69 | _ => HttpResponse::InternalServerError() 70 | .json(InternalServerError::new(&e.to_string())), 71 | }; 72 | } 73 | }; 74 | 75 | HttpResponse::Ok().json(PermissionDto::from(res)) 76 | } 77 | 78 | #[utoipa::path( 79 | get, 80 | path = "/api/v1/permissions/", 81 | params( 82 | ("text" = Option, Query, description = "The text to search for", nullable = true), 83 | ("limit" = Option, Query, description = "The limit of permissions to retrieve", nullable = true), 84 | ("page" = Option, Query, description = "The page", nullable = true), 85 | ), 86 | responses( 87 | (status = 200, description = "OK", body = Vec), 88 | (status = 204, description = "No Content"), 89 | (status = 500, description = "Internal Server Error", body = InternalServerError), 90 | ), 91 | tag = "Permissions", 92 | security( 93 | ("Token" = []) 94 | ) 95 | )] 96 | #[get("/")] 97 | #[protect("CAN_READ_PERMISSION")] 98 | pub async fn find_all_permissions( 99 | search: web::Query, 100 | pool: web::Data, 101 | ) -> HttpResponse { 102 | let search = search.into_inner(); 103 | 104 | let mut limit = search.limit; 105 | let page = search.page; 106 | 107 | let limit_clone = limit.unwrap_or(pool.server_config.max_limit); 108 | if limit.is_none() 109 | || (limit.is_some() && limit_clone > pool.server_config.max_limit || limit_clone < 1) 110 | { 111 | limit = Some(pool.server_config.max_limit); 112 | } 113 | 114 | let res = match search.text { 115 | Some(t) => { 116 | match pool 117 | .services 118 | .permission_service 119 | .search(&t, limit, page, &pool.database) 120 | .await 121 | { 122 | Ok(d) => d, 123 | Err(e) => { 124 | error!("Error while searching for permissions: {}", e); 125 | return HttpResponse::InternalServerError() 126 | .json(InternalServerError::new(&e.to_string())); 127 | } 128 | } 129 | } 130 | None => { 131 | match pool 132 | .services 133 | .permission_service 134 | .find_all(limit, page, &pool.database) 135 | .await 136 | { 137 | Ok(d) => d, 138 | Err(e) => { 139 | error!("Error while finding all permissions: {}", e); 140 | return HttpResponse::InternalServerError() 141 | .json(InternalServerError::new(&e.to_string())); 142 | } 143 | } 144 | } 145 | }; 146 | 147 | if res.is_empty() { 148 | return HttpResponse::NoContent().finish(); 149 | } 150 | 151 | let dto_list = res.iter().map(|p| p.into()).collect::>(); 152 | 153 | HttpResponse::Ok().json(dto_list) 154 | } 155 | 156 | #[utoipa::path( 157 | get, 158 | path = "/api/v1/permissions/{id}", 159 | params( 160 | ("id" = String, Path, description = "The ID of the Permission"), 161 | ), 162 | responses( 163 | (status = 200, description = "OK", body = PermissionDto), 164 | (status = 404, description = "Not Found"), 165 | (status = 500, description = "Internal Server Error", body = InternalServerError), 166 | ), 167 | tag = "Permissions", 168 | security( 169 | ("Token" = []) 170 | ) 171 | )] 172 | #[get("/{id}")] 173 | #[protect("CAN_READ_PERMISSION")] 174 | pub async fn find_by_id(path: web::Path, pool: web::Data) -> HttpResponse { 175 | let res = match pool 176 | .services 177 | .permission_service 178 | .find_by_id(&path, &pool.database) 179 | .await 180 | { 181 | Ok(d) => match d { 182 | Some(d) => d, 183 | None => return HttpResponse::NotFound().finish(), 184 | }, 185 | Err(e) => { 186 | error!("Error while finding Permission with ID {}: {}", path, e); 187 | return HttpResponse::InternalServerError() 188 | .json(InternalServerError::new(&e.to_string())); 189 | } 190 | }; 191 | 192 | HttpResponse::Ok().json(PermissionDto::from(res)) 193 | } 194 | 195 | #[utoipa::path( 196 | put, 197 | path = "/api/v1/permissions/{id}", 198 | request_body = UpdatePermission, 199 | params( 200 | ("id" = String, Path, description = "The ID of the Permission"), 201 | ), 202 | responses( 203 | (status = 200, description = "OK", body = PermissionDto), 204 | (status = 400, description = "Bad Request", body = BadRequest), 205 | (status = 404, description = "Not Found"), 206 | (status = 500, description = "Internal Server Error", body = InternalServerError), 207 | ), 208 | tag = "Permissions", 209 | security( 210 | ("Token" = []) 211 | ) 212 | )] 213 | #[put("/{id}")] 214 | #[protect("CAN_UPDATE_PERMISSION")] 215 | pub async fn update_permission( 216 | path: web::Path, 217 | update: web::Json, 218 | pool: web::Data, 219 | req: HttpRequest, 220 | ) -> HttpResponse { 221 | if update.name.is_empty() { 222 | return HttpResponse::BadRequest().json(BadRequest::new("Empty name")); 223 | } 224 | 225 | let user_id = match user_id_extractor::get_user_id_from_token(&req, &pool).await { 226 | Some(e) => e, 227 | None => { 228 | error!("Failed to get User ID from token"); 229 | return HttpResponse::InternalServerError() 230 | .json(InternalServerError::new("Failed to get User ID from token")); 231 | } 232 | }; 233 | 234 | let res = pool 235 | .services 236 | .permission_service 237 | .find_by_id(&path, &pool.database) 238 | .await; 239 | let mut permission = match res { 240 | Ok(p) => { 241 | if p.is_none() { 242 | return HttpResponse::NotFound().finish(); 243 | } 244 | 245 | p.unwrap() 246 | } 247 | Err(e) => { 248 | error!("Error while finding Permission with ID {}: {}", path, e); 249 | return HttpResponse::InternalServerError() 250 | .json(InternalServerError::new(&e.to_string())); 251 | } 252 | }; 253 | 254 | let update = update.into_inner(); 255 | 256 | permission.name = update.name; 257 | permission.description = update.description; 258 | 259 | match pool 260 | .services 261 | .permission_service 262 | .update( 263 | permission, 264 | Some(user_id), 265 | &pool.database, 266 | &pool.services.audit_service, 267 | ) 268 | .await 269 | { 270 | Ok(p) => HttpResponse::Ok().json(PermissionDto::from(p)), 271 | Err(e) => { 272 | error!("Error while updating Permission with ID {}: {}", path, e); 273 | match e { 274 | Error::NameAlreadyTaken => { 275 | HttpResponse::BadRequest().json(BadRequest::new(&e.to_string())) 276 | } 277 | _ => HttpResponse::InternalServerError() 278 | .json(InternalServerError::new(&e.to_string())), 279 | } 280 | } 281 | } 282 | } 283 | 284 | #[utoipa::path( 285 | delete, 286 | path = "/api/v1/permissions/{id}", 287 | params( 288 | ("id" = String, Path, description = "The ID of the Permission"), 289 | ), 290 | responses( 291 | (status = 200, description = "OK"), 292 | (status = 404, description = "Not Found"), 293 | (status = 500, description = "Internal Server Error", body = InternalServerError), 294 | ), 295 | tag = "Permissions", 296 | security( 297 | ("Token" = []) 298 | ) 299 | )] 300 | #[delete("/{id}")] 301 | #[protect("CAN_DELETE_PERMISSION")] 302 | pub async fn delete_permission( 303 | path: web::Path, 304 | pool: web::Data, 305 | req: HttpRequest, 306 | ) -> HttpResponse { 307 | let user_id = match user_id_extractor::get_user_id_from_token(&req, &pool).await { 308 | Some(e) => e, 309 | None => { 310 | error!("Failed to get User ID from token"); 311 | return HttpResponse::InternalServerError() 312 | .json(InternalServerError::new("Failed to get User ID from token")); 313 | } 314 | }; 315 | let res = pool 316 | .services 317 | .permission_service 318 | .delete( 319 | &path, 320 | Some(user_id), 321 | &pool.database, 322 | &pool.services.role_service, 323 | &pool.services.audit_service, 324 | ) 325 | .await; 326 | match res { 327 | Ok(_) => HttpResponse::Ok().finish(), 328 | Err(e) => match e { 329 | Error::PermissionNotFound(_) => HttpResponse::NotFound().finish(), 330 | _ => { 331 | error!("Error while deleting Permission with ID {}: {}", path, e); 332 | HttpResponse::InternalServerError().json(InternalServerError::new(&e.to_string())) 333 | } 334 | }, 335 | } 336 | } 337 | -------------------------------------------------------------------------------- /src/web/controller/role.rs: -------------------------------------------------------------------------------- 1 | pub mod role_controller; 2 | -------------------------------------------------------------------------------- /src/web/controller/user.rs: -------------------------------------------------------------------------------- 1 | pub mod user_controller; 2 | -------------------------------------------------------------------------------- /src/web/dto.rs: -------------------------------------------------------------------------------- 1 | pub mod audit; 2 | pub mod authentication; 3 | pub mod permission; 4 | pub mod role; 5 | pub mod search; 6 | pub mod user; 7 | -------------------------------------------------------------------------------- /src/web/dto/audit.rs: -------------------------------------------------------------------------------- 1 | pub mod audit_dto; 2 | -------------------------------------------------------------------------------- /src/web/dto/audit/audit_dto.rs: -------------------------------------------------------------------------------- 1 | use crate::repository::audit::audit_model::{Action, Audit, ResourceIdType, ResourceType}; 2 | use serde::{Deserialize, Serialize}; 3 | use utoipa::ToSchema; 4 | 5 | #[derive(Serialize, Deserialize, ToSchema)] 6 | pub enum ActionDto { 7 | #[serde(rename = "create")] 8 | Create, 9 | #[serde(rename = "update")] 10 | Update, 11 | #[serde(rename = "delete")] 12 | Delete, 13 | } 14 | 15 | #[derive(Serialize, Deserialize, ToSchema)] 16 | pub enum ResourceIdTypeDto { 17 | #[serde(rename = "permissionId")] 18 | PermissionId, 19 | #[serde(rename = "permissionIdVec")] 20 | PermissionIdVec, 21 | #[serde(rename = "permissionName")] 22 | PermissionName, 23 | #[serde(rename = "permissionSearch")] 24 | PermissionSearch, 25 | #[serde(rename = "roleId")] 26 | RoleId, 27 | #[serde(rename = "roleIdVec")] 28 | RoleIdVec, 29 | #[serde(rename = "roleName")] 30 | RoleName, 31 | #[serde(rename = "roleSearch")] 32 | RoleSearch, 33 | #[serde(rename = "userId")] 34 | UserId, 35 | #[serde(rename = "userName")] 36 | UserName, 37 | #[serde(rename = "userSearch")] 38 | UserSearch, 39 | #[serde(rename = "none")] 40 | None, 41 | } 42 | 43 | #[derive(Serialize, Deserialize, ToSchema)] 44 | pub enum ResourceTypeDto { 45 | #[serde(rename = "permission")] 46 | Permission, 47 | #[serde(rename = "role")] 48 | Role, 49 | #[serde(rename = "user")] 50 | User, 51 | } 52 | 53 | #[derive(Serialize, Deserialize, ToSchema)] 54 | pub struct AuditDto { 55 | pub id: String, 56 | #[serde(rename = "userId")] 57 | pub user_id: String, 58 | pub action: ActionDto, 59 | #[serde(rename = "resourceId")] 60 | pub resource_id: String, 61 | #[serde(rename = "resourceIdType")] 62 | pub resource_id_type: ResourceIdTypeDto, 63 | #[serde(rename = "resourceType")] 64 | pub resource_type: ResourceTypeDto, 65 | #[serde(rename = "createdAt")] 66 | pub created_at: String, 67 | } 68 | 69 | impl From for AuditDto { 70 | /// # Summary 71 | /// 72 | /// Convert an Audit to an AuditDto. 73 | /// 74 | /// # Arguments 75 | /// 76 | /// * `value` - An Audit. 77 | /// 78 | /// # Returns 79 | /// 80 | /// An AuditDto. 81 | fn from(value: Audit) -> Self { 82 | let action = ActionDto::from(value.action); 83 | let resource_id_type = ResourceIdTypeDto::from(value.resource_id_type); 84 | let resource_type = ResourceTypeDto::from(value.resource_type); 85 | 86 | AuditDto { 87 | id: value.id.to_hex(), 88 | user_id: value.user_id.to_hex(), 89 | action, 90 | resource_id: value.resource_id.to_hex(), 91 | resource_id_type, 92 | resource_type, 93 | created_at: value.created_at.to_rfc3339(), 94 | } 95 | } 96 | } 97 | 98 | impl From for ResourceTypeDto { 99 | /// # Summary 100 | /// 101 | /// Convert a ResourceType to a ResourceTypeDto. 102 | /// 103 | /// # Arguments 104 | /// 105 | /// * `value` - A ResourceType. 106 | /// 107 | /// # Returns 108 | /// 109 | /// A ResourceTypeDto. 110 | fn from(value: ResourceType) -> Self { 111 | match value { 112 | ResourceType::Permission => ResourceTypeDto::Permission, 113 | ResourceType::Role => ResourceTypeDto::Role, 114 | ResourceType::User => ResourceTypeDto::User, 115 | } 116 | } 117 | } 118 | 119 | impl From for ResourceIdTypeDto { 120 | /// # Summary 121 | /// 122 | /// Convert a ResourceIdType to a ResourceIdTypeDto. 123 | /// 124 | /// # Arguments 125 | /// 126 | /// * `value` - A ResourceIdType. 127 | /// 128 | /// # Returns 129 | /// 130 | /// A ResourceIdTypeDto. 131 | fn from(value: ResourceIdType) -> Self { 132 | match value { 133 | ResourceIdType::PermissionId => ResourceIdTypeDto::PermissionId, 134 | ResourceIdType::PermissionIdVec => ResourceIdTypeDto::PermissionIdVec, 135 | ResourceIdType::PermissionName => ResourceIdTypeDto::PermissionName, 136 | ResourceIdType::PermissionSearch => ResourceIdTypeDto::PermissionSearch, 137 | ResourceIdType::RoleId => ResourceIdTypeDto::RoleId, 138 | ResourceIdType::RoleIdVec => ResourceIdTypeDto::RoleIdVec, 139 | ResourceIdType::RoleName => ResourceIdTypeDto::RoleName, 140 | ResourceIdType::RoleSearch => ResourceIdTypeDto::RoleSearch, 141 | ResourceIdType::UserId => ResourceIdTypeDto::UserId, 142 | ResourceIdType::UserName => ResourceIdTypeDto::UserName, 143 | ResourceIdType::UserSearch => ResourceIdTypeDto::UserSearch, 144 | ResourceIdType::None => ResourceIdTypeDto::None, 145 | } 146 | } 147 | } 148 | 149 | impl From for ActionDto { 150 | /// # Summary 151 | /// 152 | /// Convert an Action to an ActionDto. 153 | /// 154 | /// # Arguments 155 | /// 156 | /// * `value` - An Action. 157 | /// 158 | /// # Returns 159 | /// 160 | /// An ActionDto. 161 | fn from(value: Action) -> Self { 162 | match value { 163 | Action::Create => ActionDto::Create, 164 | Action::Update => ActionDto::Update, 165 | Action::Delete => ActionDto::Delete, 166 | } 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/web/dto/authentication.rs: -------------------------------------------------------------------------------- 1 | pub mod login_request; 2 | pub mod login_response; 3 | pub mod register_request; 4 | -------------------------------------------------------------------------------- /src/web/dto/authentication/login_request.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use utoipa::ToSchema; 3 | 4 | #[derive(Deserialize, Serialize, ToSchema)] 5 | pub struct LoginRequest { 6 | pub username: String, 7 | pub password: String, 8 | } 9 | -------------------------------------------------------------------------------- /src/web/dto/authentication/login_response.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use utoipa::ToSchema; 3 | 4 | #[derive(Deserialize, Serialize, ToSchema)] 5 | pub struct LoginResponse { 6 | pub token: String, 7 | } 8 | 9 | impl LoginResponse { 10 | /// # Summary 11 | /// 12 | /// Create a new LoginResponse. 13 | /// 14 | /// # Arguments 15 | /// 16 | /// * `token` - The token of the LoginResponse. 17 | /// 18 | /// # Example 19 | /// 20 | /// ``` 21 | /// let login_response = LoginResponse::new(String::from("token")); 22 | /// ``` 23 | /// 24 | /// # Returns 25 | /// 26 | /// * `LoginResponse` - The new LoginResponse. 27 | pub fn new(token: String) -> LoginResponse { 28 | LoginResponse { token } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/web/dto/authentication/register_request.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use utoipa::ToSchema; 3 | 4 | #[derive(Serialize, Deserialize, ToSchema)] 5 | pub struct RegisterRequest { 6 | pub username: String, 7 | pub email: Option, 8 | #[serde(rename = "firstName")] 9 | pub first_name: Option, 10 | #[serde(rename = "lastName")] 11 | pub last_name: Option, 12 | pub password: String, 13 | } 14 | -------------------------------------------------------------------------------- /src/web/dto/permission.rs: -------------------------------------------------------------------------------- 1 | pub mod create_permission; 2 | pub mod permission_dto; 3 | pub mod update_permission; 4 | -------------------------------------------------------------------------------- /src/web/dto/permission/create_permission.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use utoipa::ToSchema; 3 | 4 | #[derive(Deserialize, Serialize, ToSchema)] 5 | pub struct CreatePermission { 6 | pub name: String, 7 | pub description: Option, 8 | } 9 | -------------------------------------------------------------------------------- /src/web/dto/permission/permission_dto.rs: -------------------------------------------------------------------------------- 1 | use crate::repository::permission::permission_model::Permission; 2 | use serde::{Deserialize, Serialize}; 3 | use utoipa::ToSchema; 4 | 5 | #[derive(Serialize, Deserialize, Debug, ToSchema)] 6 | pub struct PermissionDto { 7 | pub id: String, 8 | pub name: String, 9 | pub description: Option, 10 | #[serde(rename = "createdAt")] 11 | pub created_at: String, 12 | #[serde(rename = "updatedAt")] 13 | pub updated_at: String, 14 | } 15 | 16 | impl From for PermissionDto { 17 | /// # Summary 18 | /// 19 | /// Convert a Permission entity into a PermissionDto. 20 | /// 21 | /// # Arguments 22 | /// 23 | /// * `permission` - The Permission entity to be converted. 24 | /// 25 | /// # Example 26 | /// 27 | /// ``` 28 | /// let permission = Permission::new( 29 | /// String::from("id"), 30 | /// String::from("name"), 31 | /// Some(String::from("description")), 32 | /// ); 33 | /// 34 | /// let permission_dto = PermissionDto::from(permission); 35 | /// ``` 36 | /// 37 | /// # Returns 38 | /// 39 | /// * `PermissionDto` - The new PermissionDto. 40 | fn from(permission: Permission) -> Self { 41 | PermissionDto { 42 | id: permission.id.to_hex(), 43 | name: permission.name, 44 | description: permission.description, 45 | created_at: permission.created_at.to_rfc3339(), 46 | updated_at: permission.updated_at.to_rfc3339(), 47 | } 48 | } 49 | } 50 | 51 | impl From<&Permission> for PermissionDto { 52 | /// # Summary 53 | /// 54 | /// Convert a reference Permission entity into a PermissionDto. 55 | /// 56 | /// # Arguments 57 | /// 58 | /// * `permission` - The Permission entity to be converted. 59 | /// 60 | /// # Example 61 | /// 62 | /// ``` 63 | /// let permission = Permission::new( 64 | /// String::from("id"), 65 | /// String::from("name"), 66 | /// Some(String::from("description")), 67 | /// ); 68 | /// 69 | /// let permission_dto = PermissionDto::from(permission); 70 | /// ``` 71 | /// 72 | /// # Returns 73 | /// 74 | /// * `PermissionDto` - The new PermissionDto. 75 | fn from(value: &Permission) -> Self { 76 | PermissionDto { 77 | id: value.id.to_hex(), 78 | name: value.name.clone(), 79 | description: value.description.clone(), 80 | created_at: value.created_at.to_rfc3339(), 81 | updated_at: value.updated_at.to_rfc3339(), 82 | } 83 | } 84 | } 85 | 86 | #[derive(Serialize, Deserialize, ToSchema)] 87 | pub struct SimplePermissionDto { 88 | pub id: String, 89 | pub name: String, 90 | pub description: Option, 91 | } 92 | 93 | impl From for SimplePermissionDto { 94 | /// # Summary 95 | /// 96 | /// Convert a Permission entity into a SimplePermissionDto. 97 | /// 98 | /// # Arguments 99 | /// 100 | /// * `permission` - The Permission entity to be converted. 101 | /// 102 | /// # Example 103 | /// 104 | /// ``` 105 | /// let permission = Permission::new( 106 | /// String::from("id"), 107 | /// String::from("name"), 108 | /// Some(String::from("description")), 109 | /// ); 110 | /// 111 | /// let permission_dto = SimplePermissionDto::from(permission); 112 | /// ``` 113 | /// 114 | /// # Returns 115 | /// 116 | /// * `SimplePermissionDto` - The new SimplePermissionDto. 117 | fn from(permission: Permission) -> Self { 118 | SimplePermissionDto { 119 | id: permission.id.to_hex(), 120 | name: permission.name, 121 | description: permission.description, 122 | } 123 | } 124 | } 125 | 126 | impl From<&Permission> for SimplePermissionDto { 127 | /// # Summary 128 | /// 129 | /// Convert a reference Permission entity into a SimplePermissionDto. 130 | /// 131 | /// # Arguments 132 | /// 133 | /// * `permission` - The Permission entity to be converted. 134 | /// 135 | /// # Example 136 | /// 137 | /// ``` 138 | /// let permission = Permission::new( 139 | /// String::from("id"), 140 | /// String::from("name"), 141 | /// Some(String::from("description")), 142 | /// ); 143 | /// 144 | /// let permission_dto = SimplePermissionDto::from(permission); 145 | /// ``` 146 | /// 147 | /// # Returns 148 | /// 149 | /// * `SimplePermissionDto` - The new SimplePermissionDto. 150 | fn from(value: &Permission) -> Self { 151 | SimplePermissionDto { 152 | id: value.id.to_hex(), 153 | name: value.name.clone(), 154 | description: value.description.clone(), 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/web/dto/permission/update_permission.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use utoipa::ToSchema; 3 | 4 | #[derive(Serialize, Deserialize, ToSchema)] 5 | pub struct UpdatePermission { 6 | pub name: String, 7 | pub description: Option, 8 | } 9 | -------------------------------------------------------------------------------- /src/web/dto/role.rs: -------------------------------------------------------------------------------- 1 | pub mod create_role; 2 | pub mod role_dto; 3 | pub mod update_role; 4 | -------------------------------------------------------------------------------- /src/web/dto/role/create_role.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use utoipa::ToSchema; 3 | 4 | #[derive(Serialize, Deserialize, ToSchema)] 5 | pub struct CreateRole { 6 | pub name: String, 7 | pub description: Option, 8 | pub permissions: Option>, 9 | } 10 | -------------------------------------------------------------------------------- /src/web/dto/role/role_dto.rs: -------------------------------------------------------------------------------- 1 | use crate::repository::role::role_model::Role; 2 | use crate::web::dto::permission::permission_dto::{PermissionDto, SimplePermissionDto}; 3 | use serde::{Deserialize, Serialize}; 4 | use utoipa::ToSchema; 5 | 6 | #[derive(Serialize, Deserialize, ToSchema)] 7 | pub struct RoleDto { 8 | pub id: String, 9 | pub name: String, 10 | pub description: Option, 11 | pub permissions: Option>, 12 | #[serde(rename = "createdAt")] 13 | pub created_at: String, 14 | #[serde(rename = "updatedAt")] 15 | pub updated_at: String, 16 | } 17 | 18 | impl From for RoleDto { 19 | /// # Summary 20 | /// 21 | /// Convert a Role entity into a RoleDto. 22 | /// 23 | /// # Arguments 24 | /// 25 | /// * `role` - The Role entity to be converted. 26 | /// 27 | /// # Example 28 | /// 29 | /// ``` 30 | /// let role = Role::new( 31 | /// String::from("id"), 32 | /// String::from("name"), 33 | /// Some(String::from("description")), 34 | /// ); 35 | /// 36 | /// let role_dto = RoleDto::from(role); 37 | /// ``` 38 | /// 39 | /// # Returns 40 | /// 41 | /// * `RoleDto` - The new RoleDto. 42 | fn from(value: Role) -> Self { 43 | RoleDto { 44 | id: value.id.to_hex(), 45 | name: value.name, 46 | description: value.description, 47 | permissions: None, 48 | created_at: value.created_at.to_rfc3339(), 49 | updated_at: value.updated_at.to_rfc3339(), 50 | } 51 | } 52 | } 53 | 54 | impl From<&Role> for RoleDto { 55 | /// # Summary 56 | /// 57 | /// Convert a reference Role entity into a RoleDto. 58 | /// 59 | /// # Arguments 60 | /// 61 | /// * `role` - The Role entity to be converted. 62 | /// 63 | /// # Example 64 | /// 65 | /// ``` 66 | /// let role = Role::new( 67 | /// String::from("id"), 68 | /// String::from("name"), 69 | /// Some(String::from("description")), 70 | /// ); 71 | /// 72 | /// let role_dto = RoleDto::from(&role); 73 | /// ``` 74 | /// 75 | /// # Returns 76 | /// 77 | /// * `RoleDto` - The new RoleDto. 78 | fn from(value: &Role) -> Self { 79 | RoleDto { 80 | id: value.id.to_hex(), 81 | name: value.name.clone(), 82 | description: value.description.clone(), 83 | permissions: None, 84 | created_at: value.created_at.to_rfc3339(), 85 | updated_at: value.updated_at.to_rfc3339(), 86 | } 87 | } 88 | } 89 | 90 | #[derive(Serialize, Deserialize, ToSchema)] 91 | pub struct SimpleRoleDto { 92 | pub id: String, 93 | pub name: String, 94 | pub description: Option, 95 | pub permissions: Option>, 96 | } 97 | 98 | impl From for SimpleRoleDto { 99 | /// # Summary 100 | /// 101 | /// Convert a Role entity into a SimpleRoleDto. 102 | /// 103 | /// # Arguments 104 | /// 105 | /// * `role` - The Role entity to be converted. 106 | /// 107 | /// # Example 108 | /// 109 | /// ``` 110 | /// let role = Role::new( 111 | /// String::from("id"), 112 | /// String::from("name"), 113 | /// Some(String::from("description")), 114 | /// ); 115 | /// 116 | /// let role_dto = SimpleRoleDto::from(role); 117 | /// ``` 118 | /// 119 | /// # Returns 120 | /// 121 | /// * `SimpleRoleDto` - The new SimpleRoleDto. 122 | fn from(value: Role) -> Self { 123 | SimpleRoleDto { 124 | id: value.id.to_hex(), 125 | name: value.name, 126 | description: value.description, 127 | permissions: None, 128 | } 129 | } 130 | } 131 | 132 | impl From<&Role> for SimpleRoleDto { 133 | /// # Summary 134 | /// 135 | /// Convert a reference Role entity into a SimpleRoleDto. 136 | /// 137 | /// # Arguments 138 | /// 139 | /// * `role` - The Role entity to be converted. 140 | /// 141 | /// # Example 142 | /// 143 | /// ``` 144 | /// let role = Role::new( 145 | /// String::from("id"), 146 | /// String::from("name"), 147 | /// Some(String::from("description")), 148 | /// ); 149 | /// 150 | /// let role_dto = SimpleRoleDto::from(&role); 151 | /// ``` 152 | /// 153 | /// # Returns 154 | /// 155 | /// * `SimpleRoleDto` - The new SimpleRoleDto. 156 | fn from(value: &Role) -> Self { 157 | SimpleRoleDto { 158 | id: value.id.to_hex(), 159 | name: value.name.clone(), 160 | description: value.description.clone(), 161 | permissions: None, 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/web/dto/role/update_role.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use utoipa::ToSchema; 3 | 4 | #[derive(Serialize, Deserialize, ToSchema)] 5 | pub struct UpdateRole { 6 | pub name: String, 7 | pub description: Option, 8 | pub permissions: Option>, 9 | } 10 | -------------------------------------------------------------------------------- /src/web/dto/search.rs: -------------------------------------------------------------------------------- 1 | pub mod search_request; 2 | -------------------------------------------------------------------------------- /src/web/dto/search/search_request.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Deserialize, Serialize)] 4 | pub struct SearchRequest { 5 | pub text: Option, 6 | pub limit: Option, 7 | pub page: Option, 8 | } 9 | -------------------------------------------------------------------------------- /src/web/dto/user.rs: -------------------------------------------------------------------------------- 1 | pub mod create_user; 2 | pub mod update_password; 3 | pub mod update_user; 4 | pub mod user_dto; 5 | -------------------------------------------------------------------------------- /src/web/dto/user/create_user.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use utoipa::ToSchema; 3 | 4 | #[derive(Serialize, Deserialize, ToSchema)] 5 | pub struct CreateUser { 6 | pub username: String, 7 | pub email: Option, 8 | #[serde(rename = "firstName")] 9 | pub first_name: Option, 10 | #[serde(rename = "lastName")] 11 | pub last_name: Option, 12 | pub password: String, 13 | pub roles: Option>, 14 | } 15 | -------------------------------------------------------------------------------- /src/web/dto/user/update_password.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use utoipa::ToSchema; 3 | 4 | #[derive(Deserialize, Serialize, ToSchema)] 5 | pub struct UpdatePassword { 6 | #[serde(rename = "oldPassword")] 7 | pub old_password: String, 8 | #[serde(rename = "newPassword")] 9 | pub new_password: String, 10 | } 11 | 12 | #[derive(Deserialize, Serialize, ToSchema)] 13 | pub struct AdminUpdatePassword { 14 | pub password: String, 15 | } 16 | -------------------------------------------------------------------------------- /src/web/dto/user/update_user.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use utoipa::ToSchema; 3 | 4 | #[derive(Serialize, Deserialize, ToSchema)] 5 | pub struct UpdateUser { 6 | pub username: String, 7 | pub email: Option, 8 | #[serde(rename = "firstName")] 9 | pub first_name: Option, 10 | #[serde(rename = "lastName")] 11 | pub last_name: Option, 12 | pub roles: Option>, 13 | pub enabled: bool, 14 | } 15 | 16 | #[derive(Serialize, Deserialize, ToSchema)] 17 | pub struct UpdateOwnUser { 18 | pub username: String, 19 | pub email: Option, 20 | #[serde(rename = "firstName")] 21 | pub first_name: Option, 22 | #[serde(rename = "lastName")] 23 | pub last_name: Option, 24 | } 25 | -------------------------------------------------------------------------------- /src/web/dto/user/user_dto.rs: -------------------------------------------------------------------------------- 1 | use crate::repository::user::user_model::User; 2 | use crate::web::dto::role::role_dto::{RoleDto, SimpleRoleDto}; 3 | use serde::{Deserialize, Serialize}; 4 | use utoipa::ToSchema; 5 | 6 | #[derive(Serialize, Deserialize, ToSchema)] 7 | pub struct UserDto { 8 | pub id: String, 9 | pub username: String, 10 | pub email: Option, 11 | #[serde(rename = "firstName")] 12 | pub first_name: Option, 13 | #[serde(rename = "lastName")] 14 | pub last_name: Option, 15 | pub roles: Option>, 16 | #[serde(rename = "createdAt")] 17 | pub created_at: String, 18 | #[serde(rename = "updatedAt")] 19 | pub updated_at: String, 20 | pub enabled: bool, 21 | } 22 | 23 | impl From for UserDto { 24 | /// # Summary 25 | /// 26 | /// Convert a User entity into a UserDto. 27 | /// 28 | /// # Arguments 29 | /// 30 | /// * `user` - The User entity to be converted. 31 | /// 32 | /// # Example 33 | /// 34 | /// ``` 35 | /// let user = User::new( 36 | /// String::from("id"), 37 | /// String::from("username"), 38 | /// String::from("email"), 39 | /// String::from("first_name"), 40 | /// String::from("last_name"), 41 | /// String::from("password"), 42 | /// None, 43 | /// ); 44 | /// 45 | /// let user_dto = UserDto::from(user); 46 | /// ``` 47 | /// 48 | /// # Returns 49 | /// 50 | /// * `UserDto` - The new UserDto. 51 | fn from(value: User) -> Self { 52 | UserDto { 53 | id: value.id.to_hex(), 54 | username: value.username, 55 | email: value.email, 56 | first_name: value.first_name, 57 | last_name: value.last_name, 58 | roles: None, 59 | created_at: value.created_at.to_rfc3339(), 60 | updated_at: value.updated_at.to_rfc3339(), 61 | enabled: value.enabled, 62 | } 63 | } 64 | } 65 | 66 | impl From<&User> for UserDto { 67 | /// # Summary 68 | /// 69 | /// Convert a reference User entity into a UserDto. 70 | /// 71 | /// # Arguments 72 | /// 73 | /// * `user` - The User entity to be converted. 74 | /// 75 | /// # Example 76 | /// 77 | /// ``` 78 | /// let user = User::new( 79 | /// String::from("id"), 80 | /// String::from("username"), 81 | /// String::from("email"), 82 | /// String::from("first_name"), 83 | /// String::from("last_name"), 84 | /// String::from("password"), 85 | /// None, 86 | /// ); 87 | /// 88 | /// let user_dto = UserDto::from(&user); 89 | /// ``` 90 | /// 91 | /// # Returns 92 | /// 93 | /// * `UserDto` - The new UserDto. 94 | fn from(value: &User) -> Self { 95 | UserDto { 96 | id: value.id.to_hex(), 97 | username: value.username.clone(), 98 | email: value.email.clone(), 99 | first_name: value.first_name.clone(), 100 | last_name: value.last_name.clone(), 101 | roles: None, 102 | created_at: value.created_at.to_rfc3339(), 103 | updated_at: value.updated_at.to_rfc3339(), 104 | enabled: value.enabled, 105 | } 106 | } 107 | } 108 | 109 | #[derive(Serialize, Deserialize, ToSchema)] 110 | pub struct SimpleUserDto { 111 | pub id: String, 112 | pub username: String, 113 | pub email: Option, 114 | #[serde(rename = "firstName")] 115 | pub first_name: Option, 116 | #[serde(rename = "lastName")] 117 | pub last_name: Option, 118 | pub roles: Option>, 119 | } 120 | 121 | impl From for SimpleUserDto { 122 | /// # Summary 123 | /// 124 | /// Convert a User entity into a SimpleUserDto. 125 | /// 126 | /// # Arguments 127 | /// 128 | /// * `user` - The User entity to be converted. 129 | /// 130 | /// # Example 131 | /// 132 | /// ``` 133 | /// let user = User::new( 134 | /// String::from("id"), 135 | /// String::from("username"), 136 | /// String::from("email"), 137 | /// String::from("first_name"), 138 | /// String::from("last_name"), 139 | /// String::from("password"), 140 | /// None, 141 | /// ); 142 | /// 143 | /// let simple_user_dto = SimpleUserDto::from(user); 144 | /// ``` 145 | /// 146 | /// # Returns 147 | /// 148 | /// * `SimpleUserDto` - The new SimpleUserDto. 149 | fn from(value: User) -> Self { 150 | SimpleUserDto { 151 | id: value.id.to_hex(), 152 | username: value.username, 153 | email: value.email, 154 | first_name: value.first_name, 155 | last_name: value.last_name, 156 | roles: None, 157 | } 158 | } 159 | } 160 | 161 | impl From<&User> for SimpleUserDto { 162 | /// # Summary 163 | /// 164 | /// Convert a reference User entity into a SimpleUserDto. 165 | /// 166 | /// # Arguments 167 | /// 168 | /// * `user` - The User entity to be converted. 169 | /// 170 | /// # Example 171 | /// 172 | /// ``` 173 | /// let user = User::new( 174 | /// String::from("id"), 175 | /// String::from("username"), 176 | /// String::from("email"), 177 | /// String::from("first_name"), 178 | /// String::from("last_name"), 179 | /// String::from("password"), 180 | /// None, 181 | /// ); 182 | /// 183 | /// let simple_user_dto = SimpleUserDto::from(&user); 184 | /// ``` 185 | /// 186 | /// # Returns 187 | /// 188 | /// * `SimpleUserDto` - The new SimpleUserDto. 189 | fn from(value: &User) -> Self { 190 | SimpleUserDto { 191 | id: value.id.to_hex(), 192 | username: value.username.clone(), 193 | email: value.email.clone(), 194 | first_name: value.first_name.clone(), 195 | last_name: value.last_name.clone(), 196 | roles: None, 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/web/extractors.rs: -------------------------------------------------------------------------------- 1 | pub mod jwt_extractor; 2 | pub mod user_id_extractor; 3 | -------------------------------------------------------------------------------- /src/web/extractors/jwt_extractor.rs: -------------------------------------------------------------------------------- 1 | use crate::configuration::config::Config; 2 | use actix_web::dev::ServiceRequest; 3 | use actix_web::error::ErrorInternalServerError; 4 | use actix_web::Error; 5 | use log::error; 6 | use std::collections::HashSet; 7 | 8 | /// # Summary 9 | /// 10 | /// Extract the permissions from the request. 11 | /// 12 | /// # Arguments 13 | /// 14 | /// * `req` - The request to extract the permissions from. 15 | /// 16 | /// # Example 17 | /// 18 | /// ``` 19 | /// let permissions = JwtExtractor::extract(&req).await; 20 | /// ``` 21 | /// 22 | /// # Returns 23 | /// 24 | /// * `Result, Error>` - The permissions from the request. 25 | pub async fn extract(req: &ServiceRequest) -> Result, Error> { 26 | let res = match req.app_data::>() { 27 | None => { 28 | error!("Failed to get Config from request"); 29 | return Err(ErrorInternalServerError( 30 | "Failed to get Config from request", 31 | )); 32 | } 33 | Some(e) => e, 34 | }; 35 | 36 | let mut permission_list: HashSet = HashSet::::new(); 37 | if let Some(auth_header) = req.headers().get("Authorization") { 38 | if let Ok(auth_str) = auth_header.to_str() { 39 | if let Some(token) = auth_str.strip_prefix("Bearer ") { 40 | match res.services.jwt_service.verify_jwt_token(token) { 41 | Ok(subject) => { 42 | let user = match res 43 | .services 44 | .user_service 45 | .find_by_id(&subject, &res.database) 46 | .await 47 | { 48 | Ok(e) => match e { 49 | Some(e) => e, 50 | None => { 51 | return Ok(HashSet::::new()); 52 | } 53 | }, 54 | Err(e) => { 55 | error!("Failed to find user by ID: {}", e); 56 | return Ok(HashSet::::new()); 57 | } 58 | }; 59 | 60 | if !user.enabled { 61 | return Ok(HashSet::::new()); 62 | } 63 | 64 | if user.roles.is_some() { 65 | let mut role_vec: Vec = vec![]; 66 | for r in user.roles.unwrap() { 67 | role_vec.push(r.to_hex()); 68 | } 69 | 70 | let roles = match res 71 | .services 72 | .role_service 73 | .find_by_id_vec(role_vec, &res.database) 74 | .await 75 | { 76 | Ok(e) => e, 77 | Err(e) => { 78 | error!("Failed to find roles by id vec: {}", e); 79 | return Ok(HashSet::::new()); 80 | } 81 | }; 82 | 83 | if !roles.is_empty() { 84 | for r in roles { 85 | if r.permissions.is_some() { 86 | let mut oid_vec: Vec = vec![]; 87 | for r in r.permissions.unwrap() { 88 | oid_vec.push(r.to_hex()); 89 | } 90 | let permissions = match res 91 | .services 92 | .permission_service 93 | .find_by_id_vec(oid_vec, &res.database) 94 | .await 95 | { 96 | Ok(d) => d, 97 | Err(e) => { 98 | error!( 99 | "Failed to find permissions by id vec: {}", 100 | e 101 | ); 102 | continue; 103 | } 104 | }; 105 | 106 | if !permissions.is_empty() { 107 | for p in permissions { 108 | if !permission_list.contains(&p.name) { 109 | permission_list.insert(p.name); 110 | } 111 | } 112 | } 113 | } 114 | } 115 | } 116 | } 117 | } 118 | Err(e) => { 119 | error!("Failed to verify JWT token: {}", e); 120 | } 121 | } 122 | } 123 | } 124 | } 125 | 126 | Ok(permission_list) 127 | } 128 | -------------------------------------------------------------------------------- /src/web/extractors/user_id_extractor.rs: -------------------------------------------------------------------------------- 1 | use crate::configuration::config::Config; 2 | use actix_web::HttpRequest; 3 | use log::error; 4 | use mongodb::bson::oid::ObjectId; 5 | 6 | /// # Summary 7 | /// 8 | /// Get the User ID from the Authorization header. 9 | /// 10 | /// # Arguments 11 | /// 12 | /// * `req` - The HttpRequest. 13 | /// * `config` - The Config. 14 | /// 15 | /// # Example 16 | /// 17 | /// ``` 18 | /// let user_id = get_user_id_from_token(&req, &config).await; 19 | /// ``` 20 | /// 21 | /// # Returns 22 | /// 23 | /// * `Option` - The User ID. 24 | pub async fn get_user_id_from_token(req: &HttpRequest, config: &Config) -> Option { 25 | if let Some(auth_header) = req.headers().get("Authorization") { 26 | if let Ok(auth_str) = auth_header.to_str() { 27 | if let Some(token) = auth_str.strip_prefix("Bearer ") { 28 | return match config.services.jwt_service.verify_jwt_token(token) { 29 | Ok(subject) => { 30 | return match ObjectId::parse_str(subject) { 31 | Ok(e) => Some(e), 32 | Err(e) => { 33 | error!("Failed to parse Object ID: {}", e); 34 | None 35 | } 36 | }; 37 | } 38 | Err(e) => { 39 | error!("Failed to verify JWT token: {}", e); 40 | None 41 | } 42 | }; 43 | } 44 | } 45 | } 46 | 47 | None 48 | } 49 | --------------------------------------------------------------------------------