Browse Source

Merge branch 'main' into Cmskit-deployment-configuration-14083

pull/131/head
Galip Tolga Erdem 4 years ago
parent
commit
2ca827598b
  1. 7
      EShopOnAbp.sln
  2. 4
      apps/angular/src/environments/environment.ts
  3. 1
      apps/public-web/src/EShopOnAbp.PublicWeb/EShopOnAbp.PublicWeb.csproj
  4. 156
      apps/public-web/src/EShopOnAbp.PublicWeb/EShopOnAbpPublicWebModule.cs
  5. 11
      apps/public-web/src/EShopOnAbp.PublicWeb/Pages/ProductDetail.cshtml.cs
  6. 16
      apps/public-web/src/EShopOnAbp.PublicWeb/UserLoggedInEto.cs
  7. 7
      apps/public-web/src/EShopOnAbp.PublicWeb/appsettings.json
  8. 19
      etc/docker/docker-compose.infrastructure.override.yml
  9. 15
      etc/docker/docker-compose.infrastructure.yml
  10. 42
      etc/k8s/eshoponabp/values.azure.yaml
  11. 2
      gateways/web-public/src/EShopOnAbp.WebPublicGateway/appsettings.json
  12. 2
      gateways/web/src/EShopOnAbp.WebGateway/appsettings.json
  13. 2
      services/administration/src/EShopOnAbp.AdministrationService.HttpApi.Host/AdministrationServiceHttpApiHostModule.cs
  14. 11
      services/administration/src/EShopOnAbp.AdministrationService.HttpApi.Host/appsettings.json
  15. 6
      services/basket/src/EShopOnAbp.BasketService/appsettings.json
  16. 6
      services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/appsettings.json
  17. 8
      services/cmskit/src/EShopOnAbp.CmskitService.HttpApi.Host/appsettings.json
  18. 17
      services/identity/src/EShopOnAbp.IdentityService.Application.Contracts/ETOs/UserLoggedInEto.cs
  19. 98
      services/identity/src/EShopOnAbp.IdentityService.Application/UserLoggedInEventHandler.cs
  20. 10
      services/identity/src/EShopOnAbp.IdentityService.HttpApi.Host/IdentityServiceHttpApiHostModule.cs
  21. 6
      services/identity/src/EShopOnAbp.IdentityService.HttpApi.Host/appsettings.json
  22. 5
      services/ordering/src/EShopOnAbp.OrderingService.Application/Orders/OrderAppService.cs
  23. 4
      services/ordering/src/EShopOnAbp.OrderingService.HttpApi.Host/appsettings.json
  24. 4
      services/payment/src/EShopOnAbp.PaymentService.HttpApi.Host/appsettings.json
  25. 47
      shared/EShopOnAbp.Keycloak.DbMigrator/DbMigratorHostedService.cs
  26. 33
      shared/EShopOnAbp.Keycloak.DbMigrator/EShopOnAbp.Keycloak.DbMigrator.csproj
  27. 25
      shared/EShopOnAbp.Keycloak.DbMigrator/EShopOnAbpDbMigratorModule.cs
  28. 9
      shared/EShopOnAbp.Keycloak.DbMigrator/KeycloakClientOptions.cs
  29. 307
      shared/EShopOnAbp.Keycloak.DbMigrator/KeycloakDataSeeder.cs
  30. 30
      shared/EShopOnAbp.Keycloak.DbMigrator/MigrationService.cs
  31. 35
      shared/EShopOnAbp.Keycloak.DbMigrator/Program.cs
  32. 46
      shared/EShopOnAbp.Keycloak.DbMigrator/appsettings.json
  33. 1
      shared/EShopOnAbp.Shared.Hosting.Microservices/EShopOnAbpSharedHostingMicroservicesModule.cs
  34. 21
      tye.yaml

7
EShopOnAbp.sln

@ -43,6 +43,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EShopOnAbp.BasketService",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EShopOnAbp.CmskitService.HttpApi.Host", "services\cmskit\src\EShopOnAbp.CmskitService.HttpApi.Host\EShopOnAbp.CmskitService.HttpApi.Host.csproj", "{D5B9D5A5-44AA-42F8-867C-95B54780C9DC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EShopOnAbp.Keycloak.DbMigrator", "shared\EShopOnAbp.Keycloak.DbMigrator\EShopOnAbp.Keycloak.DbMigrator.csproj", "{774C6ADF-BDD0-431C-A9F3-8BAFD5A49C8C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -113,6 +115,10 @@ Global
{D5B9D5A5-44AA-42F8-867C-95B54780C9DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D5B9D5A5-44AA-42F8-867C-95B54780C9DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D5B9D5A5-44AA-42F8-867C-95B54780C9DC}.Release|Any CPU.Build.0 = Release|Any CPU
{774C6ADF-BDD0-431C-A9F3-8BAFD5A49C8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{774C6ADF-BDD0-431C-A9F3-8BAFD5A49C8C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{774C6ADF-BDD0-431C-A9F3-8BAFD5A49C8C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{774C6ADF-BDD0-431C-A9F3-8BAFD5A49C8C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -134,6 +140,7 @@ Global
{7AE4C613-780E-4DA5-9B57-76F0D40D5146} = {F415FFFD-E52D-4EBE-98DC-067C1EFFFFE3}
{E373DD66-3247-4D95-B325-064BBBC337B1} = {F415FFFD-E52D-4EBE-98DC-067C1EFFFFE3}
{D5B9D5A5-44AA-42F8-867C-95B54780C9DC} = {F415FFFD-E52D-4EBE-98DC-067C1EFFFFE3}
{774C6ADF-BDD0-431C-A9F3-8BAFD5A49C8C} = {B8B59303-2178-459B-91A8-DF353044E090}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {26F82565-C6A4-439D-93A4-3251E3E7D5B0}

4
apps/angular/src/environments/environment.ts

@ -9,11 +9,11 @@ export const environment = {
name: 'EShopOnAbp',
},
oAuthConfig: {
issuer: 'https://localhost:44330',
issuer: 'http://localhost:8080/realms/master',
redirectUri: baseUrl,
clientId: 'Web',
responseType: 'code',
scope: 'offline_access openid profile email phone AccountService IdentityService AdministrationService CatalogService OrderingService',
scope: 'offline_access openid profile email phone roles AdministrationService IdentityService BasketService CatalogService OrderingService PaymentService CmskitService',
//requireHttps: true,
},
apis: {

1
apps/public-web/src/EShopOnAbp.PublicWeb/EShopOnAbp.PublicWeb.csproj

@ -8,7 +8,6 @@
<PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="6.0.7" />
<PackageReference Include="Volo.Abp.AutoMapper" Version="6.0.0" />
<PackageReference Include="Volo.CmsKit.Domain" Version="6.0.0" />
<PackageReference Include="Volo.CmsKit.Public.Web" Version="6.0.0" />
<PackageReference Include="Yarp.ReverseProxy" Version="1.0.0" />
</ItemGroup>

156
apps/public-web/src/EShopOnAbp.PublicWeb/EShopOnAbpPublicWebModule.cs

@ -22,7 +22,14 @@ using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Polly;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication.OAuth.Claims;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using Volo.Abp;
using Volo.Abp.Account;
using Volo.Abp.AspNetCore.Authentication.OpenIdConnect;
@ -37,11 +44,13 @@ using Volo.Abp.AspNetCore.SignalR;
using Volo.Abp.AutoMapper;
using Volo.Abp.Caching;
using Volo.Abp.Caching.StackExchangeRedis;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.EventBus.RabbitMq;
using Volo.Abp.Http.Client;
using Volo.Abp.Http.Client.IdentityModel.Web;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Security.Claims;
using Volo.Abp.UI.Navigation;
using Volo.Abp.UI.Navigation.Urls;
using Volo.Abp.VirtualFileSystem;
@ -68,10 +77,7 @@ namespace EShopOnAbp.PublicWeb;
typeof(PaymentServiceHttpApiClientModule),
typeof(AbpAutoMapperModule),
typeof(CmskitServiceHttpApiClientModule),
typeof(CmsKitDomainModule),
typeof(CmsKitPublicWebModule)
)]
public class EShopOnAbpPublicWebModule : AbpModule
{
@ -142,26 +148,57 @@ public class EShopOnAbpPublicWebModule : AbpModule
.AddAbpOpenIdConnect("oidc", options =>
{
options.Authority = configuration["AuthServer:Authority"];
options.ClientId = configuration["AuthServer:ClientId"];
options.MetadataAddress = configuration["AuthServer:MetaAddress"];
options.RequireHttpsMetadata = Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]);
options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
options.ClientId = configuration["AuthServer:ClientId"];
options.ClientSecret = configuration["AuthServer:ClientSecret"];
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("role");
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("email");
options.Scope.Add("phone");
options.Scope.Add("AccountService");
options.Scope.Add("roles");
options.Scope.Add("offline_access");
options.Scope.Add("AdministrationService");
options.Scope.Add("BasketService");
options.Scope.Add("CatalogService");
options.Scope.Add("PaymentService");
options.Scope.Add("OrderingService");
options.Scope.Add("CmskitService");
options.SaveTokens = true;
//SameSite is needed for Chrome/Firefox, as they will give http error 500 back, if not set to unspecified.
// options.NonceCookie.SameSite = SameSiteMode.Unspecified;
// options.CorrelationCookie.SameSite = SameSiteMode.Unspecified;
//
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = ClaimTypes.Role,
ValidateIssuer = true
};
options.Events.OnAuthorizationCodeReceived = async (authContext) =>
{
var userLoggedInEto = CreateUserLoggedInEto(authContext.Principal, authContext.HttpContext);
if (userLoggedInEto != null)
{
var eventBus =
authContext.HttpContext.RequestServices.GetRequiredService<IDistributedEventBus>();
await eventBus.PublishAsync(userLoggedInEto);
}
};
if (AbpClaimTypes.UserName != "preferred_username")
{
options.ClaimActions.MapJsonKey(AbpClaimTypes.UserName, "preferred_username");
options.ClaimActions.DeleteClaim("preferred_username");
options.ClaimActions.RemoveDuplicate(AbpClaimTypes.UserName);
}
});
if (Convert.ToBoolean(configuration["AuthServer:IsOnProd"]))
{
context.Services.Configure<OpenIdConnectOptions>("oidc", options =>
@ -173,18 +210,21 @@ public class EShopOnAbpPublicWebModule : AbpModule
options.Events.OnRedirectToIdentityProvider = async ctx =>
{
// Intercept the redirection so the browser navigates to the right URL in your host
ctx.ProtocolMessage.IssuerAddress = configuration["AuthServer:Authority"].EnsureEndsWith('/') + "connect/authorize";
ctx.ProtocolMessage.IssuerAddress = configuration["AuthServer:Authority"].EnsureEndsWith('/') +
"connect/authorize";
if (previousOnRedirectToIdentityProvider != null)
{
await previousOnRedirectToIdentityProvider(ctx);
}
};
var previousOnRedirectToIdentityProviderForSignOut = options.Events.OnRedirectToIdentityProviderForSignOut;
var previousOnRedirectToIdentityProviderForSignOut =
options.Events.OnRedirectToIdentityProviderForSignOut;
options.Events.OnRedirectToIdentityProviderForSignOut = async ctx =>
{
// Intercept the redirection for signout so the browser navigates to the right URL in your host
ctx.ProtocolMessage.IssuerAddress = configuration["AuthServer:Authority"].EnsureEndsWith('/') + "connect/endsession";
ctx.ProtocolMessage.IssuerAddress = configuration["AuthServer:Authority"].EnsureEndsWith('/') +
"connect/endsession";
if (previousOnRedirectToIdentityProviderForSignOut != null)
{
@ -226,32 +266,6 @@ public class EShopOnAbpPublicWebModule : AbpModule
});
}
private void ConfigureBasketHttpClient(ServiceConfigurationContext context)
{
context.Services.AddStaticHttpClientProxies(
typeof(BasketServiceContractsModule).Assembly, remoteServiceConfigurationName: BasketServiceConstants.RemoteServiceName
);
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<EShopOnAbpPublicWebModule>();
});
}
private void ConfigurePayment(IConfiguration configuration)
{
Configure<EShopOnAbpPublicWebPaymentOptions>(options =>
{
options.PaymentSuccessfulCallbackUrl =
configuration["App:SelfUrl"].EnsureEndsWith('/') + "PaymentCompleted";
});
Configure<PaymentMethodUiOptions>(options =>
{
options.ConfigureIcon(PaymentMethodNames.PayPal, "fa-cc-paypal paypal");
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
@ -294,4 +308,66 @@ public class EShopOnAbpPublicWebModule : AbpModule
// endpoints.MapMetrics();
});
}
private void ConfigureBasketHttpClient(ServiceConfigurationContext context)
{
context.Services.AddStaticHttpClientProxies(
typeof(BasketServiceContractsModule).Assembly,
remoteServiceConfigurationName: BasketServiceConstants.RemoteServiceName
);
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<EShopOnAbpPublicWebModule>();
});
}
private void ConfigurePayment(IConfiguration configuration)
{
Configure<EShopOnAbpPublicWebPaymentOptions>(options =>
{
options.PaymentSuccessfulCallbackUrl =
configuration["App:SelfUrl"].EnsureEndsWith('/') + "PaymentCompleted";
});
Configure<PaymentMethodUiOptions>(options =>
{
options.ConfigureIcon(PaymentMethodNames.PayPal, "fa-cc-paypal paypal");
});
}
private UserLoggedInEto CreateUserLoggedInEto(ClaimsPrincipal principal, HttpContext httpContext)
{
var logger = httpContext.RequestServices.GetRequiredService<ILogger<EShopOnAbpPublicWebModule>>();
if (principal == null)
{
logger.LogWarning($"AuthorizationCode does not contain principal to create/update user!");
return null;
}
var claims = principal.Claims.ToList();
var userNameClaim = claims.FirstOrDefault(x => x.Type == "preferred_username");
var emailClaim = claims.FirstOrDefault(x => x.Type == ClaimTypes.Email);
var isEmailVerified = claims.FirstOrDefault(x => x.Type == "email_verified")?.Value == "true";
var phoneNumberClaim = claims.FirstOrDefault(x => x.Type == "phone");
var userIdString = claims.First(t => t.Type == ClaimTypes.NameIdentifier).Value;
if (!Guid.TryParse(userIdString, out Guid userId))
{
logger.LogWarning(
$"Handling UserLoggedInEvent... User creation failed! {userIdString} can not be parsed!");
return null;
}
return new UserLoggedInEto
{
Id = userId,
Email = emailClaim?.Value,
UserName = userNameClaim?.Value,
Phone = phoneNumberClaim?.Value,
IsEmailVerified = isEmailVerified
};
}
}

11
apps/public-web/src/EShopOnAbp.PublicWeb/Pages/ProductDetail.cshtml.cs

@ -29,8 +29,17 @@ namespace EShopOnAbp.PublicWeb.Pages
public async Task OnGet(Guid id)
{
IsPurschased = (await _orderAppService.GetMyOrdersAsync(new GetMyOrdersInput())).Any(p => p.Items.Any(p => p.ProductId == id));
Product = await _productAppService.GetAsync(id);
try
{
IsPurschased = (await _orderAppService.GetMyOrdersAsync(new GetMyOrdersInput())).Any(p => p.Items.Any(p => p.ProductId == id));
}
catch (Exception e)
{
IsPurschased = false;
Console.WriteLine(e);
}
}
}
}

16
apps/public-web/src/EShopOnAbp.PublicWeb/UserLoggedInEto.cs

@ -0,0 +1,16 @@
using System;
using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.EventBus;
namespace EShopOnAbp.PublicWeb;
[EventName("EShopOnAbp.Identity.UserLoggedIn")]
[Serializable]
public class UserLoggedInEto : EtoBase
{
public Guid Id { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string UserName { get; set; }
public bool IsEmailVerified { get; set; }
}

7
apps/public-web/src/EShopOnAbp.PublicWeb/appsettings.json

@ -36,12 +36,11 @@
"Url": "http://localhost:9200"
},
"AuthServer": {
"Authority": "https://localhost:44330",
"RequireHttpsMetadata": "true",
"Authority": "http://localhost:8080/realms/master",
"RequireHttpsMetadata": "false",
"ClientId": "PublicWeb",
"ClientSecret": "1q2w3e*",
"IsOnProd": "false",
"MetaAddress": "https://localhost:44330"
"MetaAddress": "http://localhost:8080/realms/master/.well-known/openid-configuration"
},
"ReverseProxy": {
"Routes": {

19
etc/docker/docker-compose.infrastructure.override.yml

@ -1,4 +1,4 @@
version: '3.7'
version: '3.8'
services:
rabbitmq:
@ -18,6 +18,7 @@ services:
ports:
- "5432:5432"
environment:
POSTGRES_DB: keycloak
POSTGRES_PASSWORD: "myPassw0rd"
pgadmin:
@ -26,3 +27,19 @@ services:
environment:
PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL:-pgadmin4@pgadmin.org}
PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD:-admin}
keycloak:
ports:
- "8080:8080"
environment:
DB_VENDOR: postgres
DB_ADDR: "postgres-db"
DB_DATABASE: "keycloak"
DB_USER: "postgres"
DB_PASSWORD: "myPassw0rd"
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: "1q2w3E*"
KC_HEALTH_ENABLED: "true"
entrypoint: ["/opt/keycloak/bin/kc.sh", "start-dev"]

15
etc/docker/docker-compose.infrastructure.yml

@ -1,4 +1,4 @@
version: '3.7'
version: '3.8'
services:
rabbitmq:
@ -46,12 +46,21 @@ services:
- eshoponabp-network
pgadmin:
container_name: pgadmin_container
container_name: pgadmin
image: dpage/pgadmin4:6.2
volumes:
- pgadmin_data:/var/lib/pgadmin
networks:
- eshoponabp-network
- eshoponabp-network
keycloak:
container_name: keycloak
image: quay.io/keycloak/keycloak:19.0.2
depends_on:
- postgres-db
restart: unless-stopped
networks:
- eshoponabp-network
volumes:
postgres_data:

42
etc/k8s/eshoponabp/values.azure.yaml

@ -18,8 +18,8 @@ authserver:
host: auth.eshoponabp.com
tlsSecret: eshop-wildcard-tls
image:
repository: "volocr.azurecr.io/eshoponabp/app-authserver"
tag: 1.0.5
repository: "ghcr.io/volosoft/eshoponabp/app-authserver"
tag: 1.0.0
# web sub-chart override
web:
@ -35,8 +35,8 @@ web:
ingress:
host: admin.eshoponabp.com
image:
repository: "volocr.azurecr.io/eshoponabp/app-web"
tag: 1.0.5
repository: "ghcr.io/volosoft/eshoponabp/app-web"
tag: 1.0.0
# public-web sub-chart override
public-web:
@ -56,8 +56,8 @@ public-web:
ingress:
host: eshoponabp.com
image:
repository: "volocr.azurecr.io/eshoponabp/app-publicweb"
tag: 1.0.6
repository: "ghcr.io/volosoft/eshoponabp/app-publicweb"
tag: 1.0.0
# identity-service sub-chart override
identity:
@ -92,8 +92,8 @@ identity:
ingress:
host: identity.eshoponabp.com
image:
repository: "volocr.azurecr.io/eshoponabp/service-identity"
tag: 1.0.5
repository: "ghcr.io/volosoft/eshoponabp/service-identity"
tag: 1.0.0
# administration sub-chart override
administration:
@ -119,8 +119,8 @@ administration:
ingress:
host: administration.eshoponabp.com
image:
repository: "volocr.azurecr.io/eshoponabp/service-administration"
tag: 1.0.5
repository: "ghcr.io/volosoft/eshoponabp/service-administration"
tag: 1.0.0
# gateway-web sub-chart override
gateway-web:
@ -140,8 +140,8 @@ gateway-web:
ingress:
host: gateway.eshoponabp.com
image:
repository: "volocr.azurecr.io/eshoponabp/gateway-web"
tag: 1.0.5
repository: "ghcr.io/volosoft/eshoponabp/gateway-web"
tag: 1.0.0
reRoutes:
accountService:
url: http://eshop-az-authserver
@ -172,8 +172,8 @@ gateway-web-public:
ingress:
host: gateway-public.eshoponabp.com
image:
repository: "volocr.azurecr.io/eshoponabp/gateway-web-public"
tag: 1.0.5
repository: "ghcr.io/volosoft/eshoponabp/gateway-web-public"
tag: 1.0.0
reRoutes:
accountService:
url: http://eshop-az-authserver
@ -214,8 +214,8 @@ basket:
ingress:
host: basket.eshoponabp.com
image:
repository: "volocr.azurecr.io/eshoponabp/service-basket"
tag: 1.0.5
repository: "ghcr.io/volosoft/eshoponabp/service-basket"
tag: 1.0.0
# catalog-service sub-chart override
catalog:
@ -242,8 +242,8 @@ catalog:
ingress:
host: catalog.eshoponabp.com
image:
repository: "volocr.azurecr.io/eshoponabp/service-catalog"
tag: 1.0.5
repository: "ghcr.io/volosoft/eshoponabp/service-catalog"
tag: 1.0.0
# ordering-service sub-chart override
ordering:
@ -294,7 +294,7 @@ cmskit:
image:
repository: "volocr.azurecr.io/eshoponabp/service-cmskit"
tag: 1.0.5
# payment-service sub-chart override
payment:
config:
@ -315,8 +315,8 @@ payment:
ingress:
host: payment.eshoponabp.com
image:
repository: "volocr.azurecr.io/eshoponabp/service-payment"
tag: 1.0.5
repository: "ghcr.io/volosoft/eshoponabp/service-payment"
tag: 1.0.0
# Default values for eshoponabp.
# This is a YAML-formatted file.

2
gateways/web-public/src/EShopOnAbp.WebPublicGateway/appsettings.json

@ -4,7 +4,7 @@
"CorsOrigins": "https://localhost:44335"
},
"AuthServer": {
"Authority": "https://localhost:44330",
"Authority": "http://localhost:8080/realms/master",
"RequireHttpsMetadata": "true",
"SwaggerClientId": "WebGateway_Swagger",
"SwaggerClientSecret": "1q2w3e*"

2
gateways/web/src/EShopOnAbp.WebGateway/appsettings.json

@ -4,7 +4,7 @@
"CorsOrigins": "http://localhost:4200"
},
"AuthServer": {
"Authority": "https://localhost:44330",
"Authority": "http://localhost:8080/realms/master",
"RequireHttpsMetadata": "true",
"SwaggerClientId": "WebGateway_Swagger",
"SwaggerClientSecret": "1q2w3e*"

2
services/administration/src/EShopOnAbp.AdministrationService.HttpApi.Host/AdministrationServiceHttpApiHostModule.cs

@ -91,7 +91,7 @@ public class AdministrationServiceHttpApiHostModule : AbpModule
var configuration = context.ServiceProvider.GetRequiredService<IConfiguration>();
options.SwaggerEndpoint("/swagger/v1/swagger.json", "Administration Service API");
options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]);
options.OAuthClientSecret(configuration["AuthServer:SwaggerClientSecret"]);
// options.OAuthClientSecret(configuration["AuthServer:SwaggerClientSecret"]);
});
app.UseAbpSerilogEnrichers();
app.UseAuditing();

11
services/administration/src/EShopOnAbp.AdministrationService.HttpApi.Host/appsettings.json

@ -1,13 +1,12 @@
{
"App": {
"SelfUrl": "https://localhost:44353",
"CorsOrigins": "https://localhost:44372,https://localhost:44373"
"CorsOrigins": "https://localhost:44372,https://localhost:44373,http://localhost:4200"
},
"AuthServer": {
"Authority": "https://localhost:44330",
"RequireHttpsMetadata": "true",
"SwaggerClientId": "WebGateway_Swagger",
"SwaggerClientSecret": "1q2w3e*"
"Authority": "http://localhost:8080/realms/master",
"RequireHttpsMetadata": "false",
"SwaggerClientId": "SwaggerClient"
},
"RemoteServices": {
"AbpIdentity": {
@ -20,7 +19,7 @@
"GrantType": "client_credentials",
"ClientId": "EShopOnAbp_AdministrationService",
"ClientSecret": "1q2w3e*",
"Authority": "https://localhost:44330",
"Authority": "http://localhost:8080/realms/master",
"Scope": "IdentityService"
}
},

6
services/basket/src/EShopOnAbp.BasketService/appsettings.json

@ -1,11 +1,11 @@
{
"App": {
"SelfUrl": "https://localhost:44355",
"CorsOrigins": "https://localhost:44372,https://localhost:44373,https://localhost:44335"
"CorsOrigins": "https://localhost:44372,https://localhost:44373,,http://localhost:4200"
},
"AuthServer": {
"Authority": "https://localhost:44330",
"RequireHttpsMetadata": "true",
"Authority": "http://localhost:8080/realms/master",
"RequireHttpsMetadata": "false",
"SwaggerClientId": "WebGateway_Swagger",
"SwaggerClientSecret": "1q2w3e*"
},

6
services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/appsettings.json

@ -1,11 +1,11 @@
{
"App": {
"SelfUrl": "https://localhost:44354",
"CorsOrigins": "https://localhost:44372,https://localhost:44373,https://localhost:44335,http://localhost:4200"
"CorsOrigins": "https://localhost:44372,https://localhost:44373,http://localhost:4200"
},
"AuthServer": {
"Authority": "https://localhost:44330",
"RequireHttpsMetadata": "true",
"Authority": "http://localhost:8080/realms/master",
"RequireHttpsMetadata": "false",
"SwaggerClientId": "WebGateway_Swagger",
"SwaggerClientSecret": "1q2w3e*"
},

8
services/cmskit/src/EShopOnAbp.CmskitService.HttpApi.Host/appsettings.json

@ -1,11 +1,11 @@
{
"App": {
"SelfUrl": "https://localhost:44358",
"CorsOrigins": "https://localhost:44372,https://localhost:44373"
"CorsOrigins": "https://localhost:44372,https://localhost:44373,http://localhost:4200"
},
"AuthServer": {
"Authority": "https://localhost:44330",
"RequireHttpsMetadata": "true",
"Authority": "http://localhost:8080/realms/master",
"RequireHttpsMetadata": "false",
"SwaggerClientId": "WebGateway_Swagger",
"SwaggerClientSecret": "1q2w3e*"
},
@ -51,7 +51,7 @@
"GrantType": "client_credentials",
"ClientId": "EShopOnAbp_CmskitService",
"ClientSecret": "1q2w3e*",
"Authority": "https://localhost:44330",
"Authority": "http://localhost:8080/realms/master",
"Scope": "IdentityService"
}
}

17
services/identity/src/EShopOnAbp.IdentityService.Application.Contracts/ETOs/UserLoggedInEto.cs

@ -0,0 +1,17 @@
using System;
using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.EventBus;
namespace EShopOnAbp.IdentityService.ETOs
{
[EventName("EShopOnAbp.Identity.UserLoggedIn")]
[Serializable]
public class UserLoggedInEto : EtoBase
{
public Guid Id { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string UserName { get; set; }
public bool IsEmailVerified { get; set; }
}
}

98
services/identity/src/EShopOnAbp.IdentityService.Application/UserLoggedInEventHandler.cs

@ -0,0 +1,98 @@
using System.Threading.Tasks;
using EShopOnAbp.IdentityService.ETOs;
using Microsoft.Extensions.Logging;
using Volo.Abp;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Identity;
using Volo.Abp.Uow;
namespace EShopOnAbp.IdentityService;
public class UserLoggedInEventHandler : IDistributedEventHandler<UserLoggedInEto>, ITransientDependency
{
private readonly IdentityUserManager _userManager;
private readonly ILogger<UserLoggedInEventHandler> _logger;
public UserLoggedInEventHandler(IdentityUserManager userManager, ILogger<UserLoggedInEventHandler> logger)
{
_userManager = userManager;
_logger = logger;
}
[UnitOfWork]
public async virtual Task HandleEventAsync(UserLoggedInEto eventData)
{
if (eventData == null)
{
_logger.LogWarning($"Handling UserLoggedInEvent failed! No user information found!");
return;
}
var user = await _userManager.FindByIdAsync(eventData.Id.ToString());
if (user == null)
{
await CreateCurrentUserAsync(eventData);
}
else
{
await UpdateCurrentUserAsync(user, eventData);
}
}
protected virtual async Task CreateCurrentUserAsync(UserLoggedInEto userInfo)
{
var user = new IdentityUser(
userInfo.Id,
userInfo.UserName,
userInfo.Email);
user.SetEmailConfirmed(userInfo.IsEmailVerified);
if (!string.IsNullOrEmpty(userInfo.Phone))
{
user.SetPhoneNumber(userInfo.Phone, false);
}
// This should run once to sync the admin userIds that seeded by IdentityModule and the Keycloak admin
if (userInfo.UserName == "admin")
{
var adminUser = await _userManager.FindByNameAsync("admin");
await _userManager.DeleteAsync(adminUser);
}
var result = await _userManager.CreateAsync(user);
if (!result.Succeeded)
{
throw new AbpException(string.Join('\n', result.Errors));
}
_logger.LogInformation($"Handling UserLoggedInEvent... Created new user with Id:{userInfo.Id}");
}
protected virtual async Task UpdateCurrentUserAsync(IdentityUser user, UserLoggedInEto userInfo)
{
if (user.Email != userInfo.Email)
{
_logger.LogInformation($"Handling UserLoggedInEvent... Updating the user email with:{userInfo.Email}");
await _userManager.SetEmailAsync(user, userInfo.Email);
}
if (user.PhoneNumber != userInfo.Phone)
{
_logger.LogInformation(
$"Handling UserLoggedInEvent... Updating the user phone with:{userInfo.Phone}");
await _userManager.SetPhoneNumberAsync(user, userInfo.Phone);
}
if (user.UserName != userInfo.UserName)
{
_logger.LogInformation(
$"Handling UserLoggedInEvent... Updating the user name with:{userInfo.UserName}");
await _userManager.SetUserNameAsync(user, userInfo.UserName);
}
}
}

10
services/identity/src/EShopOnAbp.IdentityService.HttpApi.Host/IdentityServiceHttpApiHostModule.cs

@ -12,6 +12,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Volo.Abp;
using Volo.Abp.Modularity;
@ -40,10 +41,9 @@ public class IdentityServiceHttpApiHostModule : AbpModule
{
{ "IdentityService", "Identity Service API" }
},
apiTitle: "IdentityService Gateway API"
apiTitle: "IdentityService API"
);
context.Services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
@ -62,6 +62,12 @@ public class IdentityServiceHttpApiHostModule : AbpModule
.AllowCredentials();
});
});
// Keycloak handles the user creation that a user name can be multiple words
Configure<IdentityOptions>(options =>
{
options.User.AllowedUserNameCharacters = null;
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)

6
services/identity/src/EShopOnAbp.IdentityService.HttpApi.Host/appsettings.json

@ -1,11 +1,11 @@
{
"App": {
"SelfUrl": "https://localhost:44351",
"CorsOrigins": "https://localhost:44372,https://localhost:44373"
"CorsOrigins": "https://localhost:44372,https://localhost:44373,http://localhost:4200"
},
"AuthServer": {
"Authority": "https://localhost:44330",
"RequireHttpsMetadata": "true",
"Authority": "http://localhost:8080/realms/master",
"RequireHttpsMetadata": "false",
"SwaggerClientId": "WebGateway_Swagger",
"SwaggerClientSecret": "1q2w3e*"
},

5
services/ordering/src/EShopOnAbp.OrderingService.Application/Orders/OrderAppService.cs

@ -40,6 +40,11 @@ public class OrderAppService : ApplicationService, IOrderAppService
[AllowAnonymous]
public async Task<List<OrderDto>> GetMyOrdersAsync(GetMyOrdersInput input)
{
if (CurrentUser.Id == null)
{
return new List<OrderDto>();
}
ISpecification<Order> specification = SpecificationFactory.Create(input.Filter);
var orders = await _orderRepository.GetOrdersByUserId(CurrentUser.GetId(), specification, true);
return CreateOrderDtoMapping(orders);

4
services/ordering/src/EShopOnAbp.OrderingService.HttpApi.Host/appsettings.json

@ -4,8 +4,8 @@
"CorsOrigins": "https://localhost:44372,https://localhost:44373,http://localhost:4200"
},
"AuthServer": {
"Authority": "https://localhost:44330",
"RequireHttpsMetadata": "true",
"Authority": "http://localhost:8080/realms/master",
"RequireHttpsMetadata": "false",
"SwaggerClientId": "WebGateway_Swagger",
"SwaggerClientSecret": "1q2w3e*"
},

4
services/payment/src/EShopOnAbp.PaymentService.HttpApi.Host/appsettings.json

@ -4,8 +4,8 @@
"CorsOrigins": "https://localhost:44372,https://localhost:44373"
},
"AuthServer": {
"Authority": "https://localhost:44330",
"RequireHttpsMetadata": "true",
"Authority": "http://localhost:8080/realms/master",
"RequireHttpsMetadata": "false",
"SwaggerClientId": "WebGateway_Swagger",
"SwaggerClientSecret": "1q2w3e*"
},

47
shared/EShopOnAbp.Keycloak.DbMigrator/DbMigratorHostedService.cs

@ -0,0 +1,47 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using Volo.Abp;
namespace EShopOnAbp.DbMigrator;
public class DbMigratorHostedService : IHostedService
{
private readonly IHostApplicationLifetime _hostApplicationLifetime;
private readonly IConfiguration _configuration;
public DbMigratorHostedService(
IHostApplicationLifetime hostApplicationLifetime,
IConfiguration configuration)
{
_hostApplicationLifetime = hostApplicationLifetime;
_configuration = configuration;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
using (var application = AbpApplicationFactory.Create<EShopOnAbpDbMigratorModule>(options =>
{
options.Services.ReplaceConfiguration(_configuration);
options.UseAutofac();
options.Services.AddLogging(c => c.AddSerilog());
}))
{
application.Initialize();
await application
.ServiceProvider
.GetRequiredService<MigrationService>()
.MigrateAsync(cancellationToken);
application.Shutdown();
_hostApplicationLifetime.StopApplication();
}
}
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}

33
shared/EShopOnAbp.Keycloak.DbMigrator/EShopOnAbp.Keycloak.DbMigrator.csproj

@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>EShopOnAbp.DbMigrator</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
<PackageReference Include="Keycloak.Net.Core" Version="1.0.20" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\EShopOnAbp.Shared.Hosting\EShopOnAbp.Shared.Hosting.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Remove="Logs\**" />
<Content Remove="Logs\**" />
<EmbeddedResource Remove="Logs\**" />
<None Remove="Logs\**" />
</ItemGroup>
<ItemGroup>
<None Remove="appsettings.json" />
<Content Include="appsettings.json">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

25
shared/EShopOnAbp.Keycloak.DbMigrator/EShopOnAbpDbMigratorModule.cs

@ -0,0 +1,25 @@
using EShopOnAbp.Shared.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
namespace EShopOnAbp.DbMigrator;
[DependsOn(
typeof(EShopOnAbpSharedHostingModule)
)]
public class EShopOnAbpDbMigratorModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
Configure<KeycloakClientOptions>(options =>
{
options.Url = configuration["Keycloak:url"];
options.AdminUserName = configuration["Keycloak:adminUsername"];
options.AdminPassword = configuration["Keycloak:adminPassword"];
options.RealmName = configuration["Keycloak:realmName"];
}
);
}
}

9
shared/EShopOnAbp.Keycloak.DbMigrator/KeycloakClientOptions.cs

@ -0,0 +1,9 @@
namespace EShopOnAbp.DbMigrator;
public class KeycloakClientOptions
{
public string Url { get; set; }
public string AdminUserName { get; set; }
public string AdminPassword { get; set; }
public string RealmName { get; set; }
}

307
shared/EShopOnAbp.Keycloak.DbMigrator/KeycloakDataSeeder.cs

@ -0,0 +1,307 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Keycloak.Net;
using Keycloak.Net.Models.Clients;
using Keycloak.Net.Models.ClientScopes;
using Keycloak.Net.Models.ProtocolMappers;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
namespace EShopOnAbp.DbMigrator;
public class KeyCloakDataSeeder : IDataSeedContributor, ITransientDependency
{
private readonly KeycloakClient _keycloakClient;
private readonly KeycloakClientOptions _keycloakOptions;
private readonly ILogger<KeyCloakDataSeeder> _logger;
private readonly IConfiguration _configuration;
public KeyCloakDataSeeder(IOptions<KeycloakClientOptions> keycloakClientOptions, ILogger<KeyCloakDataSeeder> logger,
IConfiguration configuration)
{
_logger = logger;
_configuration = configuration;
_keycloakOptions = keycloakClientOptions.Value;
_keycloakClient = new KeycloakClient(
_keycloakOptions.Url,
_keycloakOptions.AdminUserName,
_keycloakOptions.AdminPassword
);
}
public async Task SeedAsync(DataSeedContext context)
{
await UpdateAdminUserAsync();
await CreateRoleMapperAsync();
await CreateClientScopesAsync();
await CreateClientsAsync();
}
private async Task CreateRoleMapperAsync()
{
var roleScope = (await _keycloakClient.GetClientScopesAsync(_keycloakOptions.RealmName))
.FirstOrDefault(q => q.Name == "roles");
if (roleScope == null)
return;
if (!roleScope.ProtocolMappers.Any(q => q.Name == "roles"))
{
await _keycloakClient.CreateProtocolMapperAsync(_keycloakOptions.RealmName, roleScope.Id,
new ProtocolMapper()
{
Name = "roles",
Protocol = "openid-connect",
_ProtocolMapper = "oidc-usermodel-realm-role-mapper",
Config = new Dictionary<string, string>()
{
{ "access.token.claim", "true" },
{ "id.token.claim", "true" },
{ "claim.name", "roles" },
{ "multivalued", "true" },
{ "userinfo.token.claim", "true" },
}
});
}
}
private async Task CreateClientScopesAsync()
{
await CreateScopeAsync("AdministrationService");
await CreateScopeAsync("IdentityService");
await CreateScopeAsync("BasketService");
await CreateScopeAsync("CatalogService");
await CreateScopeAsync("OrderingService");
await CreateScopeAsync("PaymentService");
await CreateScopeAsync("CmskitService");
}
private async Task CreateScopeAsync(string scopeName)
{
var scope = (await _keycloakClient.GetClientScopesAsync(_keycloakOptions.RealmName))
.FirstOrDefault(q => q.Name == scopeName);
if (scope == null)
{
scope = new ClientScope
{
Name = scopeName,
Description = scopeName + " scope",
Protocol = "openid-connect",
Attributes = new Attributes
{
ConsentScreenText = scopeName,
DisplayOnConsentScreen = "true",
IncludeInTokenScope = "true"
},
ProtocolMappers = new List<ProtocolMapper>()
{
new ProtocolMapper()
{
Name = scopeName,
Protocol = "openid-connect",
_ProtocolMapper = "oidc-audience-mapper",
Config =
new
Dictionary<string,
string>() //TODO: Update when //https://github.com/AnderssonPeter/Keycloak.Net/pull/5 is merged
{
{ "id.token.claim", "false" },
{ "access.token.claim", "true" },
{ "included.custom.audience", scopeName }
}
}
}
};
await _keycloakClient.CreateClientScopeAsync(_keycloakOptions.RealmName, scope);
}
}
private async Task CreateClientsAsync()
{
await CreatePublicWebClientAsync();
await CreateSwaggerClientAsync(); // TODO: Test when Volo.Abp.Swashbuckle v6.0.1 is released (https://github.com/abpframework/abp/pull/14409)
await CreateWebClientAsync();
}
private async Task CreateWebClientAsync()
{
var webClient = (await _keycloakClient.GetClientsAsync(_keycloakOptions.RealmName, clientId: "Web"))
.FirstOrDefault();
if (webClient == null)
{
var webRootUrl = _configuration[$"Clients:Web:RootUrl"];
webClient = new Client
{
ClientId = "Web",
Name = "Angular Back-Office Web Application",
Protocol = "openid-connect",
Enabled = true,
BaseUrl = webRootUrl,
RedirectUris = new List<string>
{
$"{webRootUrl.TrimEnd('/')}"
},
FrontChannelLogout = true,
PublicClient = true
};
webClient.Attributes = new Dictionary<string, object>
{
{ "post.logout.redirect.uris", $"{webRootUrl.TrimEnd('/')}" }
};
await _keycloakClient.CreateClientAsync(_keycloakOptions.RealmName, webClient);
await AddOptionalClientScopesAsync(
"Web",
new List<string>
{
"AdministrationService", "IdentityService", "BasketService", "CatalogService",
"OrderingService", "PaymentService", "CmskitService"
}
);
}
}
private async Task CreateSwaggerClientAsync()
{
var swaggerClient =
(await _keycloakClient.GetClientsAsync(_keycloakOptions.RealmName, clientId: "SwaggerClient"))
.FirstOrDefault();
if (swaggerClient == null)
{
var webGatewaySwaggerRootUrl = _configuration[$"Clients:WebGateway:RootUrl"].TrimEnd('/');
var publicWebGatewayRootUrl = _configuration[$"Clients:PublicWebGateway:RootUrl"].TrimEnd('/');
var accountServiceRootUrl = _configuration[$"Clients:AccountService:RootUrl"].TrimEnd('/');
var identityServiceRootUrl = _configuration[$"Clients:IdentityService:RootUrl"].TrimEnd('/');
var administrationServiceRootUrl = _configuration[$"Clients:AdministrationService:RootUrl"].TrimEnd('/');
var catalogServiceRootUrl = _configuration[$"Clients:CatalogService:RootUrl"].TrimEnd('/');
var basketServiceRootUrl = _configuration[$"Clients:BasketService:RootUrl"].TrimEnd('/');
var orderingServiceRootUrl = _configuration[$"Clients:OrderingService:RootUrl"].TrimEnd('/');
var paymentServiceRootUrl = _configuration[$"Clients:PaymentService:RootUrl"].TrimEnd('/');
var cmskitServiceRootUrl = _configuration[$"Clients:CmskitService:RootUrl"].TrimEnd('/');
swaggerClient = new Client
{
ClientId = "SwaggerClient",
Name = "Swagger Client Application",
Protocol = "openid-connect",
Enabled = true,
RedirectUris = new List<string>
{
$"{webGatewaySwaggerRootUrl}/swagger/oauth2-redirect.html", // WebGateway redirect uri
$"{publicWebGatewayRootUrl}/swagger/oauth2-redirect.html", // PublicWebGateway redirect uri
$"{accountServiceRootUrl}/swagger/oauth2-redirect.html", // AccountService redirect uri
$"{identityServiceRootUrl}/swagger/oauth2-redirect.html", // IdentityService redirect uri
$"{administrationServiceRootUrl}/swagger/oauth2-redirect.html", // AdministrationService redirect uri
$"{catalogServiceRootUrl}/swagger/oauth2-redirect.html", // CatalogService redirect uri
$"{basketServiceRootUrl}/swagger/oauth2-redirect.html", // BasketService redirect uri
$"{orderingServiceRootUrl}/swagger/oauth2-redirect.html", // OrderingService redirect uri
$"{paymentServiceRootUrl}/swagger/oauth2-redirect.html", // PaymentService redirect uri
$"{cmskitServiceRootUrl}/swagger/oauth2-redirect.html" // CmskitService redirect uri
},
FrontChannelLogout = true,
PublicClient = true
};
await _keycloakClient.CreateClientAsync(_keycloakOptions.RealmName, swaggerClient);
}
}
private async Task CreatePublicWebClientAsync()
{
var publicWebClient = (await _keycloakClient.GetClientsAsync(_keycloakOptions.RealmName, clientId: "PublicWeb"))
.FirstOrDefault();
if (publicWebClient == null)
{
var publicWebRootUrl = _configuration[$"Clients:PublicWeb:RootUrl"];
publicWebClient = new Client
{
ClientId = "PublicWeb",
Name = "Public Web Application",
Protocol = "openid-connect",
Enabled = true,
BaseUrl = publicWebRootUrl,
RedirectUris = new List<string>
{
$"{publicWebRootUrl.TrimEnd('/')}/signin-oidc"
},
FrontChannelLogout = true,
PublicClient = true,
ImplicitFlowEnabled = true // for hybrid flow
};
publicWebClient.Attributes = new Dictionary<string, object>
{
{ "post.logout.redirect.uris", $"{publicWebRootUrl.TrimEnd('/')}/signout-callback-oidc" }
};
await _keycloakClient.CreateClientAsync(_keycloakOptions.RealmName, publicWebClient);
await AddOptionalClientScopesAsync(
"PublicWeb",
new List<string>
{
"AdministrationService", "IdentityService", "BasketService", "CatalogService",
"OrderingService", "PaymentService", "CmskitService"
}
);
}
}
private async Task AddOptionalClientScopesAsync(string clientName, List<string> scopes)
{
var client = (await _keycloakClient.GetClientsAsync(_keycloakOptions.RealmName, clientId: clientName))
.FirstOrDefault();
if (client == null)
{
_logger.LogError($"Couldn't find {clientName}! Could not seed optional scopes!");
return;
}
var clientOptionalScopes =
(await _keycloakClient.GetOptionalClientScopesAsync(_keycloakOptions.RealmName, client.Id)).ToList();
var clientScopes = (await _keycloakClient.GetClientScopesAsync(_keycloakOptions.RealmName)).ToList();
foreach (var scope in scopes)
{
if (!clientOptionalScopes.Any(q => q.Name == scope))
{
var serviceScope = clientScopes.First(q => q.Name == scope);
_logger.LogInformation($"Seeding {scope} scope to {clientName}.");
await _keycloakClient.UpdateOptionalClientScopeAsync(_keycloakOptions.RealmName, client.Id,
serviceScope.Id);
}
}
}
private async Task UpdateAdminUserAsync()
{
var users = await _keycloakClient.GetUsersAsync(_keycloakOptions.RealmName, username: "admin");
var adminUser = users.FirstOrDefault();
if (adminUser == null)
{
throw new Exception(
"Keycloak admin user is not provided, check if KEYCLOAK_ADMIN environment variable is passed properly.");
}
if (string.IsNullOrEmpty(adminUser.Email))
{
adminUser.Email = "admin@abp.io";
adminUser.FirstName = "admin";
adminUser.EmailVerified = true;
_logger.LogInformation("Updating admin user with email and first name...");
await _keycloakClient.UpdateUserAsync(_keycloakOptions.RealmName, adminUser.Id, adminUser);
}
}
}

30
shared/EShopOnAbp.Keycloak.DbMigrator/MigrationService.cs

@ -0,0 +1,30 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
namespace EShopOnAbp.DbMigrator;
public class MigrationService: ITransientDependency
{
private readonly ILogger<MigrationService> _logger;
private readonly IDataSeeder _dataSeeder;
public MigrationService(ILogger<MigrationService> logger, IDataSeeder dataSeeder)
{
_logger = logger;
_dataSeeder = dataSeeder;
}
public async Task MigrateAsync(CancellationToken cancellationToken)
{
// Check if keycloak api is available
//Seed data
await _dataSeeder.SeedAsync();
_logger.LogInformation("Migration completed!");
}
}

35
shared/EShopOnAbp.Keycloak.DbMigrator/Program.cs

@ -0,0 +1,35 @@
using System.Threading.Tasks;
using EShopOnAbp.DbMigrator;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Events;
class Program
{
async static Task Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
#if DEBUG
.MinimumLevel.Debug()
#else
.MinimumLevel.Information()
#endif
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning)
.Enrich.FromLogContext()
.Enrich.WithProperty("Application", $"DbMigrator")
.WriteTo.Async(c => c.File("Logs/logs.txt"))
.WriteTo.Async(c => c.Console())
.CreateLogger();
await CreateHostBuilder(args).RunConsoleAsync();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.AddAppSettingsSecretsJson()
.ConfigureLogging((context, logging) => logging.ClearProviders())
.ConfigureServices((hostContext, services) => { services.AddHostedService<DbMigratorHostedService>(); });
}

46
shared/EShopOnAbp.Keycloak.DbMigrator/appsettings.json

@ -0,0 +1,46 @@
{
"Keycloak": {
"url": "http://localhost:8080",
"adminUsername": "admin",
"adminPassword": "1q2w3E*",
"realmName": "master"
},
"Clients": {
"Web": {
"RootUrl": "http://localhost:4200"
},
"PublicWeb": {
"RootUrl": "https://localhost:44335"
},
"WebGateway": {
"RootUrl": "https://localhost:44372"
},
"PublicWebGateway": {
"RootUrl": "https://localhost:44373"
},
"AccountService": {
"RootUrl": "https://localhost:44330"
},
"IdentityService": {
"RootUrl": "https://localhost:44351"
},
"AdministrationService": {
"RootUrl": "https://localhost:44353"
},
"CatalogService": {
"RootUrl": "https://localhost:44354"
},
"BasketService": {
"RootUrl": "https://localhost:44355"
},
"OrderingService": {
"RootUrl": "https://localhost:44356"
},
"PaymentService": {
"RootUrl": "https://localhost:44357"
},
"CmskitService": {
"RootUrl": "https://localhost:44358"
}
}
}

1
shared/EShopOnAbp.Shared.Hosting.Microservices/EShopOnAbpSharedHostingMicroservicesModule.cs

@ -29,6 +29,7 @@ public class EShopOnAbpSharedHostingMicroservicesModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
var configuration = context.Services.GetConfiguration();
Configure<AbpMultiTenancyOptions>(options =>

21
tye.yaml

@ -1,13 +1,13 @@
name: EShopOnAbp
services:
- name: auth-server
project: apps/auth-server/src/EShopOnAbp.AuthServer/EShopOnAbp.AuthServer.csproj
bindings:
- protocol: https
port: 44330
env:
- Kestrel__Certificates__Default__Path=../../../../etc/dev-cert/localhost.pfx
- Kestrel__Certificates__Default__Password=8b6039b6-c67a-448b-977b-0ce6d3fcfd49
# - name: auth-server
# project: apps/auth-server/src/EShopOnAbp.AuthServer/EShopOnAbp.AuthServer.csproj
# bindings:
# - protocol: https
# port: 44330
# env:
# - Kestrel__Certificates__Default__Path=../../../../etc/dev-cert/localhost.pfx
# - Kestrel__Certificates__Default__Password=8b6039b6-c67a-448b-977b-0ce6d3fcfd49
- name: identity-service
project: services/identity/src/EShopOnAbp.IdentityService.HttpApi.Host/EShopOnAbp.IdentityService.HttpApi.Host.csproj
@ -101,4 +101,7 @@ services:
port: 44335
env:
- Kestrel__Certificates__Default__Path=../../../../etc/dev-cert/localhost.pfx
- Kestrel__Certificates__Default__Password=8b6039b6-c67a-448b-977b-0ce6d3fcfd49
- Kestrel__Certificates__Default__Password=8b6039b6-c67a-448b-977b-0ce6d3fcfd49
- name: keycloak-seeder
project: shared/EShopOnAbp.Keycloak.DbMigrator/EShopOnAbp.Keycloak.DbMigrator.csproj
Loading…
Cancel
Save