Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
df6bfc7
Added draft of Auth Service
Belgrak Feb 12, 2025
0688fdd
Added AuthService realisation, fixed StyleCop comments
Belgrak Mar 5, 2025
0ed56ba
Configured Authorization and Authentication with Gateway
Belgrak Mar 11, 2025
7674baf
Added frontend Login, Layout, Displaying themes realisation
Belgrak Mar 13, 2025
e286320
Added frontend to Docker, added filtering themes, Theme Page
Belgrak Mar 14, 2025
5263591
Bootstrap => MUI
Belgrak Mar 26, 2025
fc7464c
Fixed themes filters
Belgrak Apr 4, 2025
63040d0
Added creating themes
Belgrak Apr 4, 2025
e46fad5
Added new columns for Theme and Lecturer, added theme editing/archivi…
Belgrak Apr 11, 2025
e035493
Added RabbitMQ, Profile page, Events and Consumers on Users managing
Belgrak Apr 18, 2025
e7e350f
Added RabbitMQ for Students, Consultants, added names fields, added …
Belgrak Apr 21, 2025
db4bec8
Added require authorization for endpoinrs, fixed names in UI
Belgrak Apr 23, 2025
1751684
Fixed ProfilePage
Belgrak Apr 24, 2025
2a89026
Added more checks, fixed exceptions
Belgrak Apr 26, 2025
fecd97a
Added Refresh token
Belgrak Apr 29, 2025
127a2ea
Connected refresh token with frontend
Belgrak May 2, 2025
6244d00
Removed old files
Belgrak May 3, 2025
74d044c
Added some Auth Service tests
Belgrak May 3, 2025
86273c8
Updated Readme
Belgrak May 3, 2025
e054ea4
Added missed Designer file for migration
Belgrak May 5, 2025
56c175e
Merge remote-tracking branch 'origin/feat/auth-service' into feat/aut…
Belgrak May 5, 2025
a65936d
Added Practices pages, added Admin page, realised some helper service…
Belgrak May 8, 2025
a39ffdb
Fixed Token Refresh logic
Belgrak May 15, 2025
d1127da
Separated UserRoles, added some style, Handbook for practices
Belgrak May 16, 2025
32fdcd1
Added registration realisation
Belgrak May 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion Gateway/Gateway.Api/Gateway.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,21 @@
<UserSecretsId>bc1b7e89-5cda-4112-8b7c-ff0162e59ce3</UserSecretsId>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<DockerfileContext>..\..</DockerfileContext>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

<ItemGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.2" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.1.0" />
<PackageReference Include="Yarp.ReverseProxy" Version="2.2.0" />
</ItemGroup>
Expand Down
52 changes: 52 additions & 0 deletions Gateway/Gateway.Api/Program.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,62 @@
// <copyright file="Program.cs" company="Gleb Kargin">
// Copyright (c) Gleb Kargin. All rights reserved.
// </copyright>

using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenApi();
builder.Services.AddSwaggerGen();

byte[] key = Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"] ?? throw new InvalidOperationException("JWT Key is missing."));

builder.Services.AddAuthentication(cfg =>
{
cfg.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
cfg.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidAudience = builder.Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(key),
};
});

builder.Services.AddAuthorization(options =>
{
options.AddPolicy("Bearer", policy =>
{
policy.RequireAuthenticatedUser();
policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme);
});
});

builder.Services.AddReverseProxy().LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
builder.Services.AddCors(
options =>
{
options.AddPolicy(
"CorsPolicy",
policyBuilder => policyBuilder
.AllowAnyMethod()
.AllowCredentials()
.SetIsOriginAllowed((_) => true)
.AllowAnyHeader());
});

var app = builder.Build();

app.UseCors("CorsPolicy");

if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
Expand All @@ -16,6 +66,8 @@

app.UseHttpsRedirection();

app.UseAuthentication();
app.UseAuthorization();
app.MapReverseProxy();

app.Run();
20 changes: 20 additions & 0 deletions Gateway/Gateway.Api/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,43 @@
}
},
"AllowedHosts": "*",
"Jwt": {
"Key": "YourSuperLongSecretKeyThatIsAtLeast32Characters!",
"Issuer": "AuthService",
"Audience": "Gateway"
},
"ReverseProxy": {
"Routes": {
"CoreService": {
"ClusterId": "coreServiceCluster",
"AuthorizationPolicy": "Bearer",
"Match": { "Path": "/core-api/{**catch-all}" },
"Transforms": [
{
"PathPattern": "/api/{**catch-all}"
}
]
},
"AuthService": {
"ClusterId": "authServiceCluster",
"Match": { "Path": "/auth-api/{**catch-all}" },
"Transforms": [
{
"PathPattern": "/{**catch-all}"
}
]
}
},
"Clusters": {
"coreServiceCluster": {
"Destinations": {
"CoreService": { "Address": "http://core.api:8080/" }
}
},
"authServiceCluster": {
"Destinations": {
"AuthService": { "Address": "http://auth.api:8080/" }
}
}
}
}
Expand Down
58 changes: 58 additions & 0 deletions Helpers/GatewayAuthHandler/GatewayAuthHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// <copyright file="GatewayAuthHandler.cs" company="Gleb Kargin">
// Copyright (c) Gleb Kargin. All rights reserved.
// </copyright>

namespace GatewayAuthHandler
{
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

/// <summary>
/// Gateway authentication handler.
/// </summary>
public class GatewayAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
/// <summary>
/// Initializes a new instance of the <see cref="GatewayAuthHandler"/> class.
/// </summary>
/// <param name="options">Authenitcation scheme options.</param>
/// <param name="logger">Logger.</param>
/// <param name="encoder">Encoder.</param>
/// <param name="systemClock">System clock.</param>
public GatewayAuthHandler(
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock systemClock)
: base(options, logger, encoder, systemClock)
{
}

/// <summary>
/// Handle authenticvation method.
/// </summary>
/// <returns>Authentication result.</returns>
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!this.Request.Headers.TryGetValue("Authorization", out var authHeader))
{
return Task.FromResult(AuthenticateResult.Fail("No Authorization Header"));
}

var token = authHeader.ToString().Replace("Bearer ", string.Empty, StringComparison.OrdinalIgnoreCase);
var handler = new JwtSecurityTokenHandler();
var jwtToken = handler.ReadJwtToken(token);

var claims = jwtToken.Claims.ToList();
var identity = new ClaimsIdentity(claims, "GatewayAuth");
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, "GatewayAuth");

return Task.FromResult(AuthenticateResult.Success(ticket));
}
}
}
22 changes: 22 additions & 0 deletions Helpers/GatewayAuthHandler/GatewayAuthHandler.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication" Version="2.3.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.6.1" />
</ItemGroup>

</Project>
34 changes: 34 additions & 0 deletions PracticesService.sln
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,20 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Gateway", "Gateway", "{222D
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gateway.Api", "Gateway\Gateway.Api\Gateway.Api.csproj", "{C5116E8B-8D09-406D-80CD-0039B4AC5B68}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AuthService.Api", "Services\AuthService.Api\AuthService.Api.csproj", "{924ED870-509D-4512-A12A-1DC2E81A6E41}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Helpers", "Helpers", "{8CD65409-E7E2-4FC3-8AA5-3BFA5D08779B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GatewayAuthHandler", "Helpers\GatewayAuthHandler\GatewayAuthHandler.csproj", "{48704C05-2136-429D-A249-628CDEF16B0D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{1665257F-51D8-4832-A7ED-94603EA35B23}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shared", "Shared\Shared\Shared.csproj", "{712BF23F-5D58-4EB5-94F5-8525ED05965D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{39D921BF-8448-4A15-A870-778AEFA3EF2B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests\Tests.csproj", "{76C651ED-813E-4E68-8C0D-C6D7C354B29C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -31,13 +45,33 @@ Global
{C5116E8B-8D09-406D-80CD-0039B4AC5B68}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C5116E8B-8D09-406D-80CD-0039B4AC5B68}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C5116E8B-8D09-406D-80CD-0039B4AC5B68}.Release|Any CPU.Build.0 = Release|Any CPU
{924ED870-509D-4512-A12A-1DC2E81A6E41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{924ED870-509D-4512-A12A-1DC2E81A6E41}.Debug|Any CPU.Build.0 = Debug|Any CPU
{924ED870-509D-4512-A12A-1DC2E81A6E41}.Release|Any CPU.ActiveCfg = Release|Any CPU
{924ED870-509D-4512-A12A-1DC2E81A6E41}.Release|Any CPU.Build.0 = Release|Any CPU
{48704C05-2136-429D-A249-628CDEF16B0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{48704C05-2136-429D-A249-628CDEF16B0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{48704C05-2136-429D-A249-628CDEF16B0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{48704C05-2136-429D-A249-628CDEF16B0D}.Release|Any CPU.Build.0 = Release|Any CPU
{712BF23F-5D58-4EB5-94F5-8525ED05965D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{712BF23F-5D58-4EB5-94F5-8525ED05965D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{712BF23F-5D58-4EB5-94F5-8525ED05965D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{712BF23F-5D58-4EB5-94F5-8525ED05965D}.Release|Any CPU.Build.0 = Release|Any CPU
{76C651ED-813E-4E68-8C0D-C6D7C354B29C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{76C651ED-813E-4E68-8C0D-C6D7C354B29C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{76C651ED-813E-4E68-8C0D-C6D7C354B29C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{76C651ED-813E-4E68-8C0D-C6D7C354B29C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{94A409B2-9D11-41F4-9BB8-6B89AC26764E} = {89CC78B4-9A7B-46F4-B786-7FB0D49911B2}
{C5116E8B-8D09-406D-80CD-0039B4AC5B68} = {222D815F-4394-417D-AF31-DA86AE6E21C1}
{924ED870-509D-4512-A12A-1DC2E81A6E41} = {89CC78B4-9A7B-46F4-B786-7FB0D49911B2}
{48704C05-2136-429D-A249-628CDEF16B0D} = {8CD65409-E7E2-4FC3-8AA5-3BFA5D08779B}
{712BF23F-5D58-4EB5-94F5-8525ED05965D} = {1665257F-51D8-4832-A7ED-94603EA35B23}
{76C651ED-813E-4E68-8C0D-C6D7C354B29C} = {39D921BF-8448-4A15-A870-778AEFA3EF2B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DC6C6DEA-3EB6-439A-AE03-43698361909A}
Expand Down
104 changes: 103 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,104 @@
# PracticesService
Сервис для работы с учебными/производственными практиками кафедры

## Описание

**PracticesService** — Сервис для работы с учебными/производственными практиками кафедры. Запуск производится через Docker Compose.

## Контейнеры

- **RabbitMQ** — брокер сообщений
- **gateway.api** — шлюзовый API
- **core.api / core.db** — основной сервис и его база данных
- **auth.api / auth.db** — сервис авторизации и его база данных
- **frontend** — клиентская часть

## Предварительные требования

- [Docker](https://www.docker.com/)
- [Docker Compose](https://docs.docker.com/compose/)

## Запуск проекта

1. Клонируйте репозиторий:

```bash
git clone <URL вашего репозитория>
cd <название директории>
```

2. Запустите все сервисы:

```bash
docker-compose up --build
```

Все сервисы поднимутся автоматически, включая RabbitMQ, базы данных и API.

3. Убедитесь, что RabbitMQ доступен по адресу:
```
http://localhost:15672
```
Логин: `admin`, пароль: `admin123`

## Применение миграций для AuthService

После запуска контейнеров, необходимо применить миграции к базе данных авторизации.

1. Войдите в контейнер `auth.api`:

```bash
docker exec -it <CONTAINER_ID_ИЛИ_NAME> /bin/sh
```

Например:

```bash
docker exec -it auth.api /bin/sh
```

2. Примените миграции (предположим, используется Entity Framework CLI):

```bash
dotnet ef database update --project Services/AuthService.Api
```

> Убедитесь, что внутри контейнера установлен `dotnet-ef`. Если нет — установите или примените миграции локально и пересоберите образ.

## Доступ к сервисам

| Сервис | URL |
|--------------|----------------------------|
| Gateway API | http://localhost:5000 |
| Core API | http://localhost:5001 |
| Auth API | http://localhost:5002 |
| Frontend | http://localhost:8000 |
| RabbitMQ UI | http://localhost:15672 |

## Сеть

Все сервисы находятся в общей Docker-сети `proxybackend`.

---

## Полезные команды

- Остановка всех сервисов:

```bash
docker-compose down
```

- Просмотр логов:

```bash
docker-compose logs -f
```

- Проверка состояния контейнеров:

```bash
docker ps
```



Loading