diff --git a/api/src/Sozsoft.Platform.DbMigrator/appsettings.Dev.json b/api/src/Sozsoft.Platform.DbMigrator/appsettings.Dev.json index 09f7e0a..bffadc0 100644 --- a/api/src/Sozsoft.Platform.DbMigrator/appsettings.Dev.json +++ b/api/src/Sozsoft.Platform.DbMigrator/appsettings.Dev.json @@ -19,9 +19,7 @@ }, "Platform_App": { "ClientId": "Platform_App", - "RootUrl": "https://dev.sozsoft.com", - "TokenLifeTime": 2, - "RefreshTokenLifeTime": 8760 + "RootUrl": "https://dev.sozsoft.com" }, "Platform_Swagger": { "ClientId": "Platform_Swagger", @@ -29,9 +27,7 @@ }, "Platform_PublicApi": { "ClientId": "Platform_PublicApi", - "RootUrl": "https://dev.sozsoft.com", - "TokenLifeTime": 2, - "RefreshTokenLifeTime": 8760 + "RootUrl": "https://dev.sozsoft.com" } } } diff --git a/api/src/Sozsoft.Platform.DbMigrator/appsettings.Production.json b/api/src/Sozsoft.Platform.DbMigrator/appsettings.Production.json index ed4b4d2..5c644e5 100644 --- a/api/src/Sozsoft.Platform.DbMigrator/appsettings.Production.json +++ b/api/src/Sozsoft.Platform.DbMigrator/appsettings.Production.json @@ -19,9 +19,7 @@ }, "Platform_App": { "ClientId": "Platform_App", - "RootUrl": "https://sozsoft.com", - "TokenLifeTime": 2, - "RefreshTokenLifeTime": 8760 + "RootUrl": "https://sozsoft.com" }, "Platform_Swagger": { "ClientId": "Platform_Swagger", @@ -29,9 +27,7 @@ }, "Platform_PublicApi": { "ClientId": "Platform_PublicApi", - "RootUrl": "https://sozsoft.com", - "TokenLifeTime": 2, - "RefreshTokenLifeTime": 8760 + "RootUrl": "https://sozsoft.com" } } } diff --git a/api/src/Sozsoft.Platform.DbMigrator/appsettings.json b/api/src/Sozsoft.Platform.DbMigrator/appsettings.json index 9ce9b15..f3a6d73 100644 --- a/api/src/Sozsoft.Platform.DbMigrator/appsettings.json +++ b/api/src/Sozsoft.Platform.DbMigrator/appsettings.json @@ -20,9 +20,7 @@ }, "Platform_App": { "ClientId": "Platform_App", - "RootUrl": "http://localhost:3000", - "TokenLifeTime": 2, - "RefreshTokenLifeTime": 8760 + "RootUrl": "http://localhost:3000" }, "Platform_Swagger": { "ClientId": "Platform_Swagger", @@ -30,9 +28,7 @@ }, "Platform_PublicApi": { "ClientId": "Platform_PublicApi", - "RootUrl": "http://localhost:3000", - "TokenLifeTime": 2, - "RefreshTokenLifeTime": 8760 + "RootUrl": "http://localhost:3000" } } } diff --git a/api/src/Sozsoft.Platform.Domain/OpenIddict/OpenIddictDataSeedContributor.cs b/api/src/Sozsoft.Platform.Domain/OpenIddict/OpenIddictDataSeedContributor.cs index 7755915..172fbab 100644 --- a/api/src/Sozsoft.Platform.Domain/OpenIddict/OpenIddictDataSeedContributor.cs +++ b/api/src/Sozsoft.Platform.Domain/OpenIddict/OpenIddictDataSeedContributor.cs @@ -4,7 +4,6 @@ using System.Linq; using Volo.Abp.Uow; using Volo.Abp.Data; using System.Text.Json; -using System.Globalization; using JetBrains.Annotations; using System.Threading.Tasks; using OpenIddict.Abstractions; @@ -108,9 +107,7 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep scopes: commonScopes, redirectUri: consoleAndAngularClientRootUrl, clientUri: consoleAndAngularClientRootUrl, - postLogoutRedirectUri: consoleAndAngularClientRootUrl, - tokenLifeTime: configurationSection["Platform_App:TokenLifeTime"]?.To(), - refreshTokenLifeTime: configurationSection["Platform_App:RefreshTokenLifeTime"]?.To() + postLogoutRedirectUri: consoleAndAngularClientRootUrl ); } @@ -132,9 +129,7 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep }, scopes: commonScopes, redirectUri: $"{swaggerRootUrl}/swagger/oauth2-redirect.html", - clientUri: swaggerRootUrl, - tokenLifeTime: configurationSection["Platform_Swagger:TokenLifeTime"]?.To(), - refreshTokenLifeTime: configurationSection["Platform_Swagger:RefreshTokenLifeTime"]?.To() + clientUri: swaggerRootUrl ); } @@ -157,9 +152,7 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep scopes: commonScopes, redirectUri: publicApiClientRootUrl, clientUri: publicApiClientRootUrl, - postLogoutRedirectUri: publicApiClientRootUrl, - tokenLifeTime: configurationSection["Platform_PublicApi:TokenLifeTime"]?.To(), - refreshTokenLifeTime: configurationSection["Platform_PublicApi:RefreshTokenLifeTime"]?.To() + postLogoutRedirectUri: publicApiClientRootUrl ); } @@ -176,9 +169,7 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep string? clientUri = null, string? redirectUri = null, string? postLogoutRedirectUri = null, - List? permissions = null, - int? tokenLifeTime = 1, - int? refreshTokenLifeTime = 336 + List? permissions = null ) { if (!string.IsNullOrEmpty(secret) && string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) @@ -200,13 +191,7 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep ClientSecret = secret, ConsentType = consentType, DisplayName = displayName, - ClientUri = clientUri, - Settings = { - [OpenIddictConstants.Settings.TokenLifetimes.AccessToken] = - TimeSpan.FromHours(tokenLifeTime ?? 1).ToString("c", CultureInfo.InvariantCulture), // 1 saat - [OpenIddictConstants.Settings.TokenLifetimes.RefreshToken] = - TimeSpan.FromHours(refreshTokenLifeTime ?? 336).ToString("c", CultureInfo.InvariantCulture), // 336 saat = 14 gün - } + ClientUri = clientUri }; Check.NotNullOrEmpty(grantTypes, nameof(grantTypes)); @@ -382,6 +367,34 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep client.Permissions = JsonSerializer.Serialize(application.Permissions.Select(q => q.ToString())); await _applicationManager.UpdateAsync(client.ToModel()); } + + await RemoveTokenLifetimeSettingsAsync(client); + } + + private async Task RemoveTokenLifetimeSettingsAsync(OpenIddictApplication client) + { + if (client.Settings.IsNullOrWhiteSpace()) + { + return; + } + + var settings = JsonSerializer.Deserialize>(client.Settings); + if (settings == null) + { + return; + } + + var changed = false; + changed |= settings.Remove(OpenIddictConstants.Settings.TokenLifetimes.AccessToken); + changed |= settings.Remove(OpenIddictConstants.Settings.TokenLifetimes.RefreshToken); + + if (!changed) + { + return; + } + + client.Settings = settings.Count == 0 ? null : JsonSerializer.Serialize(settings); + await _applicationManager.UpdateAsync(client.ToModel()); } private bool HasSameRedirectUris(OpenIddictApplication existingClient, AbpApplicationDescriptor application) diff --git a/api/src/Sozsoft.Platform.HttpApi.Host/PlatformHttpApiHostModule.cs b/api/src/Sozsoft.Platform.HttpApi.Host/PlatformHttpApiHostModule.cs index 4c2fb53..e37d2a4 100644 --- a/api/src/Sozsoft.Platform.HttpApi.Host/PlatformHttpApiHostModule.cs +++ b/api/src/Sozsoft.Platform.HttpApi.Host/PlatformHttpApiHostModule.cs @@ -83,12 +83,14 @@ public class PlatformHttpApiHostModule : AbpModule { public override void PreConfigureServices(ServiceConfigurationContext context) { + var configuration = context.Services.GetConfiguration(); + PreConfigure(builder => { builder.AddServer(server => { - server.SetAccessTokenLifetime(TimeSpan.FromMinutes(60)); - server.SetRefreshTokenLifetime(TimeSpan.FromMinutes(90)); + server.SetAccessTokenLifetime(TimeSpan.FromMinutes(configuration.GetValue("OpenIddict:TokenLifetimes:AccessTokenMinutes", 60))); + server.SetRefreshTokenLifetime(TimeSpan.FromMinutes(configuration.GetValue("OpenIddict:TokenLifetimes:RefreshTokenMinutes", 90))); server.AddEventHandler(builder => builder.UseScopedHandler()); }); diff --git a/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicFormReport.cs b/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicFormReport.cs index daa88b7..e24ab21 100644 --- a/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicFormReport.cs +++ b/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicFormReport.cs @@ -78,6 +78,7 @@ public class DynamicFormReport : XtraReport var localizer = scope.ServiceProvider.GetRequiredService>(); var configuration = scope.ServiceProvider.GetRequiredService(); var currentUser = scope.ServiceProvider.GetRequiredService(); + var isSubReport = DynamicReportImageHelper.IsSubReport(parameters); if (!await authManager.CanAccess(listFormCode)) { @@ -119,7 +120,7 @@ public class DynamicFormReport : XtraReport ConfigurePage(); ConfigureDataSource(selectQuery, connectionString, dataSourceType); - await BuildLayoutAsync(listForm, reportFields, row, localizer, DynamicReportImageHelper.GetImageBaseUrl(configuration), currentUser.UserName); + await BuildLayoutAsync(listForm, reportFields, row, localizer, DynamicReportImageHelper.GetImageBaseUrl(configuration), currentUser.UserName, isSubReport); RequestParameters = false; } @@ -158,7 +159,8 @@ public class DynamicFormReport : XtraReport IDictionary row, IStringLocalizer localizer, string imageBaseUrl, - string currentUserName) + string currentUserName, + bool isSubReport) { Bands.Clear(); var formGroups = GetFormGroups(listForm, fields).ToList(); @@ -179,7 +181,10 @@ public class DynamicFormReport : XtraReport Font = new DXFont("Arial", 14F, DXFontStyle.Bold), TextAlignment = TextAlignment.MiddleLeft }); - DynamicReportImageHelper.AddGeneratedAtToHeader(reportHeader.Controls, pageWidth); + if (!isSubReport) + { + DynamicReportImageHelper.AddGeneratedAtToHeader(reportHeader.Controls, pageWidth); + } DynamicReportImageHelper.AddFooterTopLine(bottomMargin.Controls, pageWidth); DynamicReportImageHelper.AddCurrentUserNameToFooter(bottomMargin.Controls, currentUserName, pageWidth); @@ -667,7 +672,8 @@ public class DynamicFormReport : XtraReport return new Dictionary { ["listFormCode"] = subForm.Code, - ["filter"] = filter + ["filter"] = filter, + [DynamicReportImageHelper.IsSubReportParameterName] = bool.TrueString }; } diff --git a/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicGridReport.cs b/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicGridReport.cs index f2914e4..8739aa8 100644 --- a/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicGridReport.cs +++ b/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicGridReport.cs @@ -71,6 +71,7 @@ public class DynamicGridReport : XtraReport var localizer = scope.ServiceProvider.GetRequiredService>(); var configuration = scope.ServiceProvider.GetRequiredService(); var currentUser = scope.ServiceProvider.GetRequiredService(); + var isSubReport = DynamicReportImageHelper.IsSubReport(parameters); if (!await authManager.CanAccess(listFormCode)) { @@ -156,7 +157,7 @@ public class DynamicGridReport : XtraReport selectQuery, connectionString, dataSourceType); - BuildLayout(listForm, reportColumns, localizer, imageBaseUrl, currentUser.UserName, criteriaText); + BuildLayout(listForm, reportColumns, localizer, imageBaseUrl, currentUser.UserName, criteriaText, isSubReport); RequestParameters = false; } @@ -245,7 +246,8 @@ public class DynamicGridReport : XtraReport IStringLocalizer localizer, string imageBaseUrl, string currentUserName, - string criteriaText) + string criteriaText, + bool isSubReport) { Bands.Clear(); @@ -271,7 +273,10 @@ public class DynamicGridReport : XtraReport Font = new DXFont("Arial", 14F, DXFontStyle.Bold), TextAlignment = TextAlignment.MiddleLeft }); - DynamicReportImageHelper.AddGeneratedAtToHeader(reportHeader.Controls, pageWidth); + if (!isSubReport) + { + DynamicReportImageHelper.AddGeneratedAtToHeader(reportHeader.Controls, pageWidth); + } DynamicReportImageHelper.AddReportCriteriaToHeader(reportHeader.Controls, criteriaText, pageWidth); DynamicReportImageHelper.AddFooterTopLine(bottomMargin.Controls, pageWidth); diff --git a/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicReportImageHelper.cs b/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicReportImageHelper.cs index 075a122..8e30b36 100644 --- a/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicReportImageHelper.cs +++ b/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicReportImageHelper.cs @@ -25,6 +25,7 @@ namespace Sozsoft.Reports.PredefinedReports; internal static class DynamicReportImageHelper { + public const string IsSubReportParameterName = "isSubReport"; private const float DefaultGridImageHeight = 82F; private const float DefaultGridImageWidth = 86F; private const float DefaultFormImageHeight = 86F; @@ -394,6 +395,13 @@ internal static class DynamicReportImageHelper return parameters.TryGetValue(name, out var value) ? value : null; } + public static bool IsSubReport(IDictionary parameters) + { + var value = GetParameter(parameters, IsSubReportParameterName); + return string.Equals(value, bool.TrueString, StringComparison.OrdinalIgnoreCase) || + string.Equals(value, "1", StringComparison.OrdinalIgnoreCase); + } + public static string GetImageBaseUrl(IConfiguration configuration) { return configuration["App:CdnUrl"] ?? configuration["App:SelfUrl"]; diff --git a/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicTreeReport.cs b/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicTreeReport.cs index a372444..b6cb6c7 100644 --- a/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicTreeReport.cs +++ b/api/src/Sozsoft.Platform.HttpApi.Host/PredefinedReports/DynamicTreeReport.cs @@ -75,6 +75,7 @@ public class DynamicTreeReport : XtraReport var localizer = scope.ServiceProvider.GetRequiredService>(); var configuration = scope.ServiceProvider.GetRequiredService(); var currentUser = scope.ServiceProvider.GetRequiredService(); + var isSubReport = DynamicReportImageHelper.IsSubReport(parameters); if (!await authManager.CanAccess(listFormCode)) { @@ -166,7 +167,7 @@ public class DynamicTreeReport : XtraReport ConfigurePage(reportColumns); ConfigureDataSource(orderedRows, reportColumns); - BuildLayout(listForm, reportColumns, localizer, imageBaseUrl, currentUser.UserName, criteriaText); + BuildLayout(listForm, reportColumns, localizer, imageBaseUrl, currentUser.UserName, criteriaText, isSubReport); RequestParameters = false; } @@ -417,7 +418,8 @@ public class DynamicTreeReport : XtraReport IStringLocalizer localizer, string imageBaseUrl, string currentUserName, - string criteriaText) + string criteriaText, + bool isSubReport) { Bands.Clear(); @@ -443,7 +445,10 @@ public class DynamicTreeReport : XtraReport Font = new DXFont("Arial", 14F, DXFontStyle.Bold), TextAlignment = TextAlignment.MiddleLeft }); - DynamicReportImageHelper.AddGeneratedAtToHeader(reportHeader.Controls, pageWidth); + if (!isSubReport) + { + DynamicReportImageHelper.AddGeneratedAtToHeader(reportHeader.Controls, pageWidth); + } DynamicReportImageHelper.AddReportCriteriaToHeader(reportHeader.Controls, criteriaText, pageWidth); DynamicReportImageHelper.AddFooterTopLine(bottomMargin.Controls, pageWidth); diff --git a/api/src/Sozsoft.Platform.HttpApi.Host/appsettings.Dev.json b/api/src/Sozsoft.Platform.HttpApi.Host/appsettings.Dev.json index 8f71de3..f0c28d6 100644 --- a/api/src/Sozsoft.Platform.HttpApi.Host/appsettings.Dev.json +++ b/api/src/Sozsoft.Platform.HttpApi.Host/appsettings.Dev.json @@ -23,6 +23,12 @@ "RequireHttpsMetadata": false, "SwaggerClientId": "Platform_Swagger" }, + "OpenIddict": { + "TokenLifetimes": { + "AccessTokenMinutes": 60, + "RefreshTokenMinutes": 90 + } + }, "StringEncryption": { "DefaultPassPhrase": "UQpiYfT79zRZ3yYH" }, diff --git a/api/src/Sozsoft.Platform.HttpApi.Host/appsettings.Production.json b/api/src/Sozsoft.Platform.HttpApi.Host/appsettings.Production.json index cf5e516..8930c7d 100644 --- a/api/src/Sozsoft.Platform.HttpApi.Host/appsettings.Production.json +++ b/api/src/Sozsoft.Platform.HttpApi.Host/appsettings.Production.json @@ -23,6 +23,12 @@ "RequireHttpsMetadata": false, "SwaggerClientId": "Platform_Swagger" }, + "OpenIddict": { + "TokenLifetimes": { + "AccessTokenMinutes": 60, + "RefreshTokenMinutes": 90 + } + }, "StringEncryption": { "DefaultPassPhrase": "UQpiYfT79zRZ3yYH" }, @@ -35,4 +41,4 @@ "Default": "Information" } } -} \ No newline at end of file +} diff --git a/api/src/Sozsoft.Platform.HttpApi.Host/appsettings.json b/api/src/Sozsoft.Platform.HttpApi.Host/appsettings.json index c69e309..45482d2 100644 --- a/api/src/Sozsoft.Platform.HttpApi.Host/appsettings.json +++ b/api/src/Sozsoft.Platform.HttpApi.Host/appsettings.json @@ -6,7 +6,7 @@ "RedirectAllowedUrls": "http://localhost:4200,http://localhost:4200/authentication/callback", "AttachmentsPath": "C:\\Private\\Projects\\sozsoft-platform\\configs\\mail-queue\\attachments", "CdnUrl": "http://localhost:4005", - "CdnPath": "C:\\Private\\Projects\\sozsoft-platform\\configs\\docker\\data\\cdn", + "CdnPath": "D:\\Sozsoft\\sozsoft-platform\\configs\\docker\\data\\cdn", "Version": "1.0.5", "BackupPath": "/var/opt/mssql/backup" }, @@ -23,6 +23,12 @@ "RequireHttpsMetadata": false, "SwaggerClientId": "Platform_Swagger" }, + "OpenIddict": { + "TokenLifetimes": { + "AccessTokenMinutes": 60, + "RefreshTokenMinutes": 90 + } + }, "StringEncryption": { "DefaultPassPhrase": "UQpiYfT79zRZ3yYH" }, diff --git a/ui/src/services/auth.service.ts b/ui/src/services/auth.service.ts index aab76d2..3015218 100644 --- a/ui/src/services/auth.service.ts +++ b/ui/src/services/auth.service.ts @@ -47,17 +47,31 @@ export const refreshToken = (refresh_token: string) => authConfig, ) -export const signOut = (data: any) => +const revokeToken = (token: string, tokenTypeHint: 'access_token' | 'refresh_token') => apiService.fetchData( { method: 'POST', url: '/connect/revocat', headers: { 'content-type': 'application/x-www-form-urlencoded' }, data: new URLSearchParams({ - token: data.token, + token, client_id: 'Platform_App', - token_type_hint: 'access_token', + token_type_hint: tokenTypeHint, }).toString(), }, authConfig, ) + +export const signOut = (data: { token?: string; refreshToken?: string }) => { + const requests = [] + + if (data.token) { + requests.push(revokeToken(data.token, 'access_token')) + } + + if (data.refreshToken) { + requests.push(revokeToken(data.refreshToken, 'refresh_token')) + } + + return Promise.all(requests) +} diff --git a/ui/src/services/platformApi.service.ts b/ui/src/services/platformApi.service.ts index 50d3de7..6107868 100644 --- a/ui/src/services/platformApi.service.ts +++ b/ui/src/services/platformApi.service.ts @@ -2,7 +2,7 @@ import axios from 'axios' import { store } from '../store' import { jwtDecode } from 'jwt-decode' import appConfig from '../proxy/configs/app.config' -import { refreshToken } from './auth.service' +import { refreshToken, signOut as revokeTokens } from './auth.service' import { isLoginSuccess } from '../proxy/account/models' const unauthorizedCode = [401] @@ -103,11 +103,19 @@ platformApiService.interceptors.response.use( error.config.headers['Authorization'] = `Bearer ${access_token}` return axios.request(error.config) } else { + await revokeTokens({ + token: auth.session.token, + refreshToken: auth.session.refreshToken, + }).catch(() => undefined) setIsRefreshing(false) signOut() error.silent = true } } catch (error2) { + await revokeTokens({ + token: auth.session.token, + refreshToken: auth.session.refreshToken, + }).catch(() => undefined) setIsRefreshing(false) signOut() error.silent = true diff --git a/ui/src/utils/hooks/useAuth.ts b/ui/src/utils/hooks/useAuth.ts index ea6adc9..26c320d 100644 --- a/ui/src/utils/hooks/useAuth.ts +++ b/ui/src/utils/hooks/useAuth.ts @@ -23,7 +23,7 @@ function useAuth() { const query = useQuery() - const { token, signedIn } = useStoreState((state) => state.auth.session) + const { token, refreshToken, signedIn } = useStoreState((state) => state.auth.session) const handleToken = ({ token, @@ -135,7 +135,7 @@ function useAuth() { } const _signOut = async () => { - await signOut({ token }) + await signOut({ token, refreshToken }) signOutStore() navigate(appConfig.unAuthenticatedEntryPath) }