Skip to content
Merged
Show file tree
Hide file tree
Changes from 62 commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
a0e4b37
Refactor: Move Shared models to Contracts to fix Blazor WASM references
Jan 24, 2026
e29c50b
Fix: Remove redundant using directives for Contracts.Models
Jan 24, 2026
1562ac3
Fix: Remove remaining duplicate using directives in Modules
Jan 24, 2026
a162d28
Fix(Auth): Update admin role mapping and add safety tests
Jan 24, 2026
1fe00eb
Chore: Add auth diagnostics middleware
Jan 24, 2026
0302227
Fix: Update namespace references and add debug auth constants
Jan 24, 2026
2ac5c46
Refactor: Complete code review fixes - RoleNames constants, localizat…
Jan 24, 2026
8ed5e98
Fix: Re-add Shared.Utilities.Constants for ValidationConstants access
Jan 24, 2026
41f3d7a
Fix: Add missing AuthConstants using statements in middleware and ext…
Jan 24, 2026
e19ebad
feat: Implement security extensions for environment-specific authenti…
Jan 24, 2026
a09f6ca
fix: resolve authorization 403 errors by registering KeycloakPermissi…
Jan 27, 2026
d8aa569
test: fix 7 failing unit tests to expect Portuguese error messages
Jan 27, 2026
e400389
refactor: implement code review fixes for security and authorization
Jan 27, 2026
1600a03
feat: Implement Keycloak-based user permission resolution and add a q…
Jan 27, 2026
7ca1371
test: fix GetProvidersByIdsQueryHandlerTests to expect Portuguese err…
Jan 27, 2026
029607d
feat: Add PII masking utility and architecture tests for the authoriz…
Jan 27, 2026
d77b12f
feat: Introduce multiple provider query handlers with unit tests and …
Jan 27, 2026
788516d
feat: introduce `GetProvidersByStateQueryHandler` with dedicated unit…
Jan 27, 2026
b3972c8
feat: introduce BaseApiTest as a base class for integration tests, pr…
Jan 27, 2026
1e0ffdf
feat: Add base test authentication handler and application startup di…
Jan 27, 2026
7046cf5
feat: Add `BaseTestAuthenticationHandler` for configurable test authe…
Jan 27, 2026
370d9c8
feat: Introduce `TestContainerFixture` to manage shared TestContainer…
Jan 27, 2026
1ec304a
Fix: Global API Protocol Mismatch (500) and Route Mismatch (404). Bac…
Jan 28, 2026
7f788ff
fix: align Allowed Cities DTOs between frontend and backend
Jan 28, 2026
d74a58a
fix: address code review comments and fix unit tests
Jan 28, 2026
7852909
chore: remove accidental test_results.txt
Jan 28, 2026
bafd3ed
feat: introduce service and service category management operations, l…
Jan 28, 2026
fb77921
feat: Introduce centralized validation messages, an admin endpoint fo…
Jan 28, 2026
d535fb1
feat: Implement service and service category command handlers and the…
Jan 28, 2026
6744fa3
feat: Add integration and unit tests for Providers, Users, Service Ca…
Jan 28, 2026
619280e
feat: Add comprehensive unit and integration tests for ServiceCatalog…
Jan 28, 2026
14dc43f
feat: add integration tests for Providers and ServiceCatalogs modules…
Jan 28, 2026
77ad1ce
feat: Introduce comprehensive integration and end-to-end tests for mu…
Jan 28, 2026
929a92b
feat: Introduce E2E tests and a standardized HTTP status code convent…
Jan 29, 2026
df88701
feat: Add E2E tests for the Providers module covering CRUD, workflow,…
Jan 29, 2026
53eaa8b
feat: introduce AllowedCity entity and add unit tests for provider pr…
Jan 29, 2026
45ebaf8
test(allowedcity): add unit tests for AllowedCity entity constructor,…
Jan 29, 2026
6e1f4ee
test: add unit tests for provider and service command handlers.
Jan 29, 2026
9075979
feat: Add unit tests for shared components, endpoint extensions, exce…
Jan 29, 2026
aac2aac
feat: Implement provider activation command handler and add unit test…
Jan 29, 2026
50f3495
feat: implement E2E tests to verify correct HTTP status codes for val…
Jan 29, 2026
3a3bdd7
feat: Implement AllowedCity entity with full CRUD operations and add …
Jan 30, 2026
8ef0d75
feat: Implement AllowedCity entity with full CRUD, activation, and de…
Jan 31, 2026
e6afe09
feat: Implement service creation dialog with Fluxor actions and E2E t…
Jan 31, 2026
f95c2a2
feat: Add comprehensive unit tests for `EndpointExtensions` covering …
Jan 31, 2026
819e31f
feat: Implement creation and update of allowed cities with geocoding …
Jan 31, 2026
36f1650
feat: Add create and update handlers for allowed cities, including un…
Jan 31, 2026
2d169bf
feat: add unit tests for `UpdateAllowedCityHandler` covering various …
Feb 1, 2026
f6a9813
feat: Implement AllowedCity CRUD operations with dedicated API endpoi…
Feb 1, 2026
5a415c4
feat: add admin endpoint to update allowed cities
Feb 2, 2026
f903664
feat: Add E2E tests for Locations module's admin AllowedCities endpoi…
Feb 2, 2026
574cc78
feat: Add an endpoint to update allowed cities and introduce end-to-e…
Feb 2, 2026
c08858b
feat: implement Locations module with allowed city management and Nom…
Feb 3, 2026
53cc5a4
refactor(locations): implement code review fixes and optimizations
Feb 3, 2026
9d24593
feat: Add administrative functionality for managing allowed cities, i…
Feb 3, 2026
4c2609d
feat: Add comprehensive provider administration and service managemen…
Feb 3, 2026
db3bcbc
feat: Add comprehensive provider administration and service managemen…
Feb 3, 2026
64cc3ae
feat: Add module APIs, admin dashboard, document upload, location man…
Feb 3, 2026
b1da169
Update src/Shared/Exceptions/GlobalExceptionHandler.cs
frigini Feb 4, 2026
697a835
feat: Implement module APIs for Documents, Users, SearchProviders, an…
Feb 4, 2026
c847fc0
feat: Add handler for updating allowed cities and implement developme…
Feb 4, 2026
dfd81d4
feat: Introduce document upload command handler, development data see…
Feb 4, 2026
5110b11
feat: implement UpdateAllowedCityHandler and DevelopmentDataSeeder.
Feb 4, 2026
9991ad9
feat: Implement DevelopmentDataSeeder to populate the database with d…
Feb 5, 2026
937c064
Update src/Modules/Documents/Application/ModuleApi/DocumentsModuleApi.cs
frigini Feb 5, 2026
fc3bf0d
feat: add document upload command handler, validation messages, and u…
Feb 5, 2026
9ff326f
feat: Add admin UI for providers, documents, and dashboard, along wit…
Feb 5, 2026
4c888b2
feat: Add initial Admin Portal with Dashboard, Providers, and Documen…
Feb 5, 2026
1aa945b
feat: Introduce Admin Portal architecture and overview documentation,…
Feb 5, 2026
53b209c
feat: Add initial comprehensive documentation for the Admin Portal, i…
Feb 5, 2026
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
300 changes: 32 additions & 268 deletions .editorconfig

Large diffs are not rendered by default.

114 changes: 88 additions & 26 deletions infrastructure/keycloak/realms/meajudaai-realm.dev.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,33 +28,64 @@
"accountTheme": "meajudaai",
"emailTheme": "meajudaai",
"internationalizationEnabled": true,
"supportedLocales": ["pt-BR", "en"],
"supportedLocales": [
"pt-BR",
"en"
],
"defaultLocale": "pt-BR",
"roles": {
"realm": [
{
"name": "admin",
"description": "Administrador total da plataforma"
"description": "Administrador total da plataforma - todas as permissões"
},
{
"name": "meajudaai-system-admin",
"description": "Administrador do sistema - todas as permissões"
},
{
"name": "meajudaai-user-admin",
"description": "Administrador de usuários - gerenciar usuários"
},
{
"name": "meajudaai-user-operator",
"description": "Operador de usuários - leitura e atualização"
},
{
"name": "meajudaai-user",
"description": "Usuário básico - perfil próprio"
},
{
"name": "meajudaai-provider-admin",
"description": "Administrador de prestadores - CRUD completo"
},
{
"name": "meajudaai-provider",
"description": "Prestador - apenas leitura"
},
{
"name": "meajudaai-order-admin",
"description": "Administrador de pedidos - CRUD completo"
},
{
"name": "provider-manager",
"description": "Gerente de provedores - pode criar, editar e deletar provedores"
"name": "meajudaai-order-operator",
"description": "Operador de pedidos - leitura e atualização"
},
{
"name": "document-reviewer",
"description": "Revisor de documentos - pode revisar e aprovar documentos"
"name": "meajudaai-report-admin",
"description": "Administrador de relatórios - criar e exportar"
},
{
"name": "catalog-manager",
"description": "Gerente de catálogo - pode gerenciar serviços e categorias"
"name": "meajudaai-report-viewer",
"description": "Visualizador de relatórios - apenas visualizar"
},
{
"name": "operator",
"description": "Operador com leitura/escrita limitada"
"name": "meajudaai-catalog-manager",
"description": "Gerente de catálogo - gerenciar serviços e categorias"
},
{
"name": "viewer",
"description": "Visualizador somente leitura"
"name": "meajudaai-location-manager",
"description": "Gerente de localidades - gerenciar cidades e estados"
Comment thread
frigini marked this conversation as resolved.
},
{
"name": "customer",
Expand Down Expand Up @@ -149,30 +180,61 @@
}
}
]
},
{
"clientId": "meajudaai-api-service",
"name": "MeAjudaAi API Service Account",
"description": "Service account for API to access Keycloak Admin API",
"enabled": true,
"publicClient": false,
"serviceAccountsEnabled": true,
"standardFlowEnabled": false,
"implicitFlowEnabled": false,
"directAccessGrantsEnabled": false,
"protocol": "openid-connect",
"attributes": {
"access.token.lifespan": "3600"
},
"secret": "meajudaai-api-secret-dev-2024",
"authorizationServicesEnabled": false
Comment thread
frigini marked this conversation as resolved.
}
],
"users": [
{
"username": "service-account-meajudaai-api-service",
"enabled": true,
"serviceAccountClientId": "meajudaai-api-service",
"clientRoles": {
"realm-management": [
"view-users",
"query-users",
"view-realm"
]
}
},
{
"username": "admin.portal",
"enabled": true,
"firstName": "Admin",
"lastName": "Portal",
"email": "admin@meajudaai.dev",
"emailVerified": true,
"realmRoles": [
"admin",
"meajudaai-system-admin",
"meajudaai-user-admin",
"meajudaai-provider-admin",
"meajudaai-order-admin",
"meajudaai-report-admin",
"meajudaai-catalog-manager",
"meajudaai-location-manager"
],
"credentials": [
{
"type": "password",
"value": "admin123",
"temporary": true
"temporary": false
}
],
"realmRoles": [
"admin",
"provider-manager",
"document-reviewer",
"catalog-manager",
"operator",
"viewer"
]
},
{
Expand All @@ -182,16 +244,16 @@
"lastName": "Demo",
"email": "customer@meajudaai.dev",
"emailVerified": true,
"realmRoles": [
"customer"
],
"credentials": [
{
"type": "password",
"value": "customer123",
"temporary": true
"temporary": false
}
],
"realmRoles": [
"customer"
]
}
]
}
}
163 changes: 163 additions & 0 deletions infrastructure/keycloak/reimport-realm.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
# Script para reimportar o realm do Keycloak
# Este script deleta o realm existente e reimporta com as novas configurações
#
# Uso:
# .\reimport-realm.ps1 [-ShowSecrets] [-KeycloakUrl <url>] [-RealmName <name>] [-RealmFile <path>]
#
# Variáveis de ambiente:
# KEYCLOAK_ADMIN_USER - Nome de usuário do admin (padrão: "admin")
# KEYCLOAK_CLIENT_ID - Client ID para autenticação (padrão: "admin-cli")
# KEYCLOAK_URL - URL do Keycloak (padrão: "http://localhost:8080")
# REALM_NAME - Nome do realm (padrão: "meajudaai")
# REALM_FILE - Caminho do arquivo de configuração do realm

param(
[switch]$ShowSecrets = $false,
[string]$KeycloakUrl = $env:KEYCLOAK_URL,
[string]$RealmName = $env:REALM_NAME,
[string]$RealmFile = $env:REALM_FILE
)

# Configurações padrão
if ([string]::IsNullOrWhiteSpace($KeycloakUrl)) {
$KeycloakUrl = "http://localhost:8080"
}

if ([string]::IsNullOrWhiteSpace($RealmName)) {
$RealmName = "meajudaai"
}

if ([string]::IsNullOrWhiteSpace($RealmFile)) {
$RealmFile = ".\infrastructure\keycloak\realms\meajudaai-realm.dev.json"
}

# Validar que o arquivo existe
if (-not (Test-Path $RealmFile)) {
Write-Error "Arquivo de realm não encontrado: $RealmFile"
exit 1
}

# Obter credenciais de ambiente ou prompt
$adminUser = $env:KEYCLOAK_ADMIN_USER
if ([string]::IsNullOrWhiteSpace($adminUser)) {
$adminUser = "admin"
}

$clientId = $env:KEYCLOAK_CLIENT_ID
if ([string]::IsNullOrWhiteSpace($clientId)) {
$clientId = "admin-cli"
}

# Solicitar senha de forma segura
$adminPassword = Read-Host -Prompt "Digite a senha do admin do Keycloak" -AsSecureString
$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($adminPassword)
$adminPasswordPlain = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
[System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR)

Write-Host "Reimportando realm do Keycloak..." -ForegroundColor Cyan
Write-Host "URL: $KeycloakUrl"
Write-Host "Realm: $RealmName"
Write-Host "Arquivo: $RealmFile"
if ($ShowSecrets) {
Write-Host "Admin User: $adminUser"
Write-Host "Client ID: $clientId"
}
Write-Host ""

# 1. Obter token de admin
Write-Host "1. Obtendo token de administrador..." -ForegroundColor Yellow

$tokenBody = @{
username = $adminUser
password = $adminPasswordPlain
grant_type = "password"
client_id = $clientId
}
Comment thread
frigini marked this conversation as resolved.

try {
$tokenResponse = Invoke-RestMethod -Uri "$KeycloakUrl/realms/master/protocol/openid-connect/token" `
-Method Post `
-ContentType "application/x-www-form-urlencoded" `
-Body $tokenBody `
-ErrorAction Stop

$ADMIN_TOKEN = $tokenResponse.access_token
Write-Host "Token obtido com sucesso" -ForegroundColor Green
}
catch {
Write-Error "Erro ao obter token de admin. Verifique se o Keycloak está rodando e as credenciais estão corretas."
Write-Error $_.Exception.Message
exit 1
}
finally {
# Limpar senha da memória
$adminPasswordPlain = $null
$tokenBody = $null
}

Write-Host ""

# 2. Deletar realm existente (se existir)
Write-Host "2. Deletando realm existente (se existir)..." -ForegroundColor Yellow

$headers = @{
Authorization = "Bearer $ADMIN_TOKEN"
}

try {
Invoke-RestMethod -Uri "$KeycloakUrl/admin/realms/$RealmName" `
-Method Delete `
-Headers $headers `
-ErrorAction Stop
Write-Host "Realm deletado com sucesso" -ForegroundColor Green
}
catch {
if ($_.Exception.Response.StatusCode -eq 404) {
Write-Host "Realm não existia, continuando..." -ForegroundColor Gray
}
else {
Write-Error "Erro ao deletar realm: $($_.Exception.Message)"
exit 1
}
Comment thread
frigini marked this conversation as resolved.
}

Write-Host ""

# 3. Importar novo realm
Write-Host "3. Importando novo realm..." -ForegroundColor Yellow

$realmContent = Get-Content -Path $RealmFile -Raw -ErrorAction Stop

try {
Invoke-RestMethod -Uri "$KeycloakUrl/admin/realms" `
-Method Post `
-Headers $headers `
-ContentType "application/json" `
-Body $realmContent `
-ErrorAction Stop

Write-Host "Realm importado com sucesso!" -ForegroundColor Green
Write-Host ""
Write-Host "ATENÇÃO: Configurações de segurança necessárias:" -ForegroundColor Yellow
Write-Host ""
Write-Host "1. Configure o Client Secret para 'meajudaai-api-service':" -ForegroundColor Cyan
Write-Host " - Acesse: $KeycloakUrl/admin/master/console/#/$RealmName/clients"
Write-Host " - Selecione 'meajudaai-api-service'"
Write-Host " - Vá para a aba 'Credentials'"
Write-Host " - Gere ou configure um novo secret"
Write-Host " - Atualize a variável de ambiente KEYCLOAK_CLIENT_SECRET na aplicação"
Write-Host ""
Write-Host "2. Configure senhas para os usuários:" -ForegroundColor Cyan
Write-Host " - Acesse: $KeycloakUrl/admin/master/console/#/$RealmName/users"
Write-Host " - Para cada usuário (admin.portal, customer.demo):"
Write-Host " * Clique no usuário"
Write-Host " * Vá para 'Credentials'"
Write-Host " * Defina uma senha"
Write-Host ""
Write-Host "Reimportação concluída! Reinicie a aplicação Aspire após configurar as credenciais." -ForegroundColor Green
}
catch {
Write-Error "Erro ao importar realm."
Write-Error $_.Exception.Message
exit 1
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,33 @@ private static Ok<ClientConfiguration> GetClientConfiguration(
// Normalizar URL (remover trailing slash)
apiBaseUrl = apiBaseUrl.TrimEnd('/');

// Configuração do Keycloak
var keycloakAuthority = configuration["Keycloak:Authority"]
?? throw new InvalidOperationException("Keycloak:Authority não configurado");
// Configuração do Keycloak - suportar tanto o novo formato (BaseUrl + Realm) quanto o legado (Authority)
var keycloakAuthority = configuration["Keycloak:Authority"]?.TrimEnd('/');

if (string.IsNullOrWhiteSpace(keycloakAuthority))
{







// Construir Authority a partir de BaseUrl e Realm
var keycloakBaseUrl = configuration["Keycloak:BaseUrl"];
if (string.IsNullOrWhiteSpace(keycloakBaseUrl))
throw new InvalidOperationException("Keycloak:BaseUrl ou Keycloak:Authority deve estar configurado");

keycloakBaseUrl = keycloakBaseUrl.TrimEnd('/');

var keycloakRealm = configuration["Keycloak:Realm"];
if (string.IsNullOrWhiteSpace(keycloakRealm))
keycloakRealm = "meajudaai"; // Valor padrão

keycloakRealm = keycloakRealm.Trim('/');

keycloakAuthority = $"{keycloakBaseUrl}/realms/{keycloakRealm}";
}

var keycloakClientId = configuration["Keycloak:ClientId"]
?? throw new InvalidOperationException("Keycloak:ClientId não configurado");
Expand Down
Loading
Loading