Welcome to the backend repository of the Leave a Note App.
This repository contains the ASP.NET Core project with Entity Framework that serves as the backend for Leave a Note App. This README will provide you with essential information to understand and work with the backend portion of the application.
- Tech Stack
- Getting Started
- Project Structure
- Database
- Authentication and Authorization
- Environments and Data Security
- API Reference
- Author and Feedback
Server: ASP.NET 7 Core, Entity Framework 7
Database: Azure SQL Server and Database
Hosting: Microsoft Azure
Before you begin, make sure you have the following prerequisites installed:
- Visual Studio Minimum version -> 17.4
- .NET SDK 7.0
- SQL Express
- Clone this repository to your local machine.
- Create a new SQL database and copy the connection string.
- Open the solution in visual studio and run the following command in the Package Manager Console:
dotnet restore
- Open the Package Manager Console an run this command:
cd ./LeaveANoteServerProject
- Add the following secrets to the user's secrets by typing this command into the Package Manager Console:
dotnet user-secrets set "Your example Key" "Your example value".
- JWT Key:
dotnet user-secrets set "JWTKEY" "Your secret key"
- Connection string:
dotnet user-secrets set "ConnectionsStrings:defaultConnection" "Your sql connection string"
- You can view your new secrets using this command:
dotnet user-secrets list
- Migrate with entity framework cli to create the database with the following command in the console:
dotnet ef database update
- Now You can go to the database and see your tables.
- Now you can run the project and test the endpoints start with register a new user and then try to login.
- Controllers: This folder contains the API controllers that handle incoming HTTP requests and produce appropriate responses.
- Models: The models folder holds the data models that represent the entities and objects used within the application.
- Data: This folder holds the data context class.
- Services: In the services folder, you'll find the business logic and services that encapsulate various operations within the application.
- Utils: This folder contains the Token class.
- DTO's: DTOs are used to define the shape of data that is transferred between different parts of the application, you'll find a subfolder of dto's for each entity.
- Migrations: he migrations folder is often associated with database management. In the context of Entity Framework, migrations are scripts that represent changes to the database schema over time. They are used to apply changes to the database structure, such as creating or altering tables, columns, and relationships
- Each row represents a user of the application.
- Users can have multiple accidents associated with them.
- The relationship is one-to-many from Users to Accidents.
- '*' = Uniqe Index
Column | Type | Description |
---|---|---|
Id | int | Primary key |
Name | nvarchar | User's name |
nvarchar | User's email* | |
Password | nvarchar | User's password |
CarNumber | nvarchar | User's car number* |
PhoneNumber | nvarchar | User's phone number* |
Role | nvarchar | User's role |
DeviceToken | nvarchar | User's device token |
CreatedAt | datetime | Timestamp when user was created |
- Each row represents an accident report or note.
- An accident is associated with the damaged user.
- Each accident can be associated with zero or one unmatched report.
- The relationship is many-to-one from Accidents to Users.
- The relationship is one-to-one from Accidents to UnmatchedReports.
Column | Type | Description |
---|---|---|
Id | int | Primary key |
HittingDriverName | nvarchar | Name of the hitting driver in the accident |
HittingCarNumber | nvarchar | Car number of the hitting vehicle |
HittingDriverPhoneNumber | nvarchar | Phone number of the hitting driver |
ReporterName | nvarchar | Name of the reporter |
ReporterPhoneNumber | nvarchar | Phone number of the reporter |
ImageSource | nvarchar | Image source of the accident |
Type | nvarchar | Type of accident |
IsAnonymous | bit | Flag indicating if the report is anonymous |
IsIdentify | bit | Flag indicating if the hitting driver is identified |
IsDeleted | bit | Flag indicating if the accident is deleted |
IsRead | bit | Flag indicating if the accident is read |
Date | nvarchar | Date of the accident |
createdAt | datetime | Timestamp when accident was created |
UserId | int (FK) | Foreign key referencing Users table |
- Each row represents an unmatched report related to an accident.
- Each unmatched report is associated with one accident.
- The relationship is one-to-one from UnmatchedReports to Accidents.
Column | Type | Description |
---|---|---|
Id | int | Primary key |
DamagedCarNumber | nvarchar | Car number of the damaged vehicle |
AccidentId | int (FK) | Foreign key referencing Accidents table |
CreatedAt | datetime | Timestamp when unmatched report was created |
I've implemented authentication using JWT (JSON Web Tokens) Bearer tokens. When a user successfully logs in, they receive a JWT token that is included in the header of subsequent API requests. This token is used to verify the user's identity and permissions for each request.
Upon registration, each user is automatically assigned the "User" role. Roles provide a way to group users with similar permissions. In the application, there are two primary roles: "User" and "Admin".
Role-based authorization is enforced to control access to specific parts of the application. Here's how it works:
-
User Role:
- Users with the "User" role can access most of the application's endpoints.
- They can create accident reports, view their own reports, and perform other user-specific actions.
-
Admin Role:
- Users with the "Admin" role have elevated privileges.
- The "stats" controller, which contains administrative statistics , is accessible only to users with the "Admin" role.
- This is achieved using
[Authorize(Roles = "Admin")]
on the relevant controller or action methods.
To restrict access to the "stats" controller for administrators only, Iv'e used the [Authorize(Roles = "Admin")]
attribute on the relevant controller or specific action methods. This ensures that only users with the "Admin" role can access these endpoints.
[ApiController]
[Route("api/[controller]")]
[Authorize(Roles = "Admin")] // Restrict access to users with the "Admin" role
public class StatsController : ControllerBase
{
// stats controller endpoints..
}
To enhance security and manage sensitive configuration information such as connection strings and JWT keys, we leverage Azure Managed Identity and Azure Key Vault.
-
Managed Identity: The application, when running in an Azure App Service, uses Managed Identity to authenticate itself to Azure services. This means that the application's code doesn't need to manage or store credentials. Instead, it can use the identity to securely access Azure Key Vault.
-
Azure Key Vault: We store secrets like the connection string and JWT key securely in Azure Key Vault. Azure Key Vault acts as a secure store for these secrets, and our application can access them only with proper authorization.
Our application's configuration differs between production and development environments to accommodate security and operational considerations.
In the production environment:
- The Azure Key Vault URL is retrieved from an environment variable set in the Azure App Service configuration.
- We use the
ManagedIdentityCredential
to authenticate with Azure Key Vault and retrieve secrets like the connection string and JWT key. - These secrets are then used to configure the application's services and authentication.
In the development environment:
- Configuration values are retrieved from the local
user-secrets
we have set up before. - This includes connection strings and JWT keys for local development purposes.
- The
builder.Configuration["ConnectionsStrings:defaultConnection"]
andbuilder.Configuration["JWTKEY"]
methods are used to access these values.
By utilizing Managed Identity, Azure Key Vault, and environment-specific configuration, we ensure that sensitive information remains secure and that our application is properly configured for both production and development environments. This approach enhances security, simplifies configuration management, and follows best practices for Azure-based applications.
Make sure to consistently maintain and monitor the configuration to ensure the highest level of security and operational efficiency.
Base Url: "api/[controller]"
Registers a new user and returns a JWT token.
- URL:
/api/user/register
- Method:
POST
- Headers: None (AllowAnonymous)
- Request Body:
User
object: User registration details
Logs in a user and returns a JWT token.
- URL:
/api/user/login
- Method:
POST
- Headers: None (AllowAnonymous)
- Request Body:
LoginDto
object: User login credentials
Retrieves a list of all users.
- URL:
/api/user/allUsers
- Method:
GET
- Headers: None (AllowAnonymous)
Updates the device token of a user.
- URL:
/api/user/updateDeviceToken
- Method:
PUT
- Headers: Authorization Bearer Token (User must be authenticated)
- Request Body:
DeviceTokenUpdateDto
object: User ID and updated device token
Updates the information of a user.
- URL:
/api/user/informationUpdate
- Method:
PUT
- Headers: Authorization Bearer Token (User must be authenticated)
- Request Body:
UpdateInformationDto
object: User ID and updated information
Updates the password of a user.
- URL:
/api/user/passwordUpdate
- Method:
PUT
- Headers: Authorization Bearer Token (User must be authenticated)
- Request Body:
UpdateUserPasswordDto
object: User ID, old password, and new password
Retrieves a user by their car number.
- URL:
/api/user/searchCarNumber/
- Method:
GET
- Headers: Authorization Bearer Token (User must be authenticated)
- Query Parameter:
carNumber
(string): Car number to search for
Retrieves a user by their ID.
- URL:
/api/user/getById/
- Method:
GET
- Headers: Authorization Bearer Token (User must be authenticated)
- Query Parameters:
id
(int): User IDminimal
(bool): Whether to retrieve minimal user information
Marks an accident message as read in the user's inbox.
- URL:
/api/user/readMessageInbox
- Method:
PUT
- Headers: Authorization Bearer Token (User must be authenticated)
- Request Body:
AccidentDeleteDto
object: User ID and accident ID
Deletes an accident message from the user's history.
- URL:
/api/user/deleteMessage
- Method:
PUT
- Headers: Authorization Bearer Token (User must be authenticated)
- Request Body:
AccidentDeleteDto
object: User ID and accident ID
Deletes a user by their ID.
- URL:
/api/user/deleteUser/{id}
- Method:
DELETE
- Headers: Authorization Bearer Token (User must be authenticated)
Base Url: "api/[controller]"
Creates a new accident note.
- URL:
/api/accident/createNote
- Method:
POST
- Headers: Authorization Bearer Token (User must be authenticated)
- Request Body:
CreateNoteReqDto
object: Details for creating an accident note
Creates a new accident report.
- URL:
/api/accident/createReport
- Method:
POST
- Headers: Authorization Bearer Token (User must be authenticated)
- Request Body:
CreateReportReqDto
object: Details for creating an accident report
Base Url: "api/[controller]"
Retrieves data about registered users for a specific year.
- URL:
/api/stats/registeredUsersData
- Method:
GET
- Headers: Authorization Bearer Token with Admin Role (User must be authenticated and have Admin role)
- Query Parameter:
year
(int): The year for which to retrieve registered users data
Retrieves data about the distribution of accident reports.
- URL:
/api/stats/reportsDistribution/
- Method:
GET
- Headers: Authorization Bearer Token with Admin Role (User must be authenticated and have Admin role)
If you have any feedback, please reach out to me at korenkaplan96@gmail.com.
Developed by @korenkaplan