diff --git a/docs/en/Migration-Guides/OpenIddict-Angular.md b/docs/en/Migration-Guides/OpenIddict-Angular.md index c3ca6a7fbb..fe0b3986ac 100644 --- a/docs/en/Migration-Guides/OpenIddict-Angular.md +++ b/docs/en/Migration-Guides/OpenIddict-Angular.md @@ -112,7 +112,7 @@ ## IdentityServer -This project is renamed to **AuthServer** after v6.0.0-rc1. You can also refactor and rename your project to *AuthServer* for easier updates in the future. +This project is renamed to **AuthServer** after v6.0.0. You can also refactor and rename your project to *AuthServer* for easier updates in the future. - In **MyApplication.IdentityServer.csproj** replace **project references**: diff --git a/docs/en/Migration-Guides/OpenIddict-Blazor-Server.md b/docs/en/Migration-Guides/OpenIddict-Blazor-Server.md index 65011f6124..93c9562a4d 100644 --- a/docs/en/Migration-Guides/OpenIddict-Blazor-Server.md +++ b/docs/en/Migration-Guides/OpenIddict-Blazor-Server.md @@ -103,7 +103,7 @@ ## IdentityServer -This project is renamed to **AuthServer** after v6.0.0-rc1. You can also refactor and rename your project to *AuthServer* for easier updates in the future. +This project is renamed to **AuthServer** after v6.0.0. You can also refactor and rename your project to *AuthServer* for easier updates in the future. - In **MyApplication.IdentityServer.csproj** replace **project references**: diff --git a/docs/en/Migration-Guides/OpenIddict-Blazor.md b/docs/en/Migration-Guides/OpenIddict-Blazor.md index 7db082808e..09e137a0a6 100644 --- a/docs/en/Migration-Guides/OpenIddict-Blazor.md +++ b/docs/en/Migration-Guides/OpenIddict-Blazor.md @@ -131,7 +131,7 @@ ## IdentityServer -This project is renamed to **AuthServer** after v6.0.0-rc1. You can also refactor and rename your project to *AuthServer* for easier updates in the future. +This project is renamed to **AuthServer** after v6.0.0. You can also refactor and rename your project to *AuthServer* for easier updates in the future. - In **MyApplication.IdentityServer.csproj** replace **project references**: diff --git a/docs/en/Migration-Guides/OpenIddict-Mvc.md b/docs/en/Migration-Guides/OpenIddict-Mvc.md index 7fd411155d..8dd5ec6c94 100644 --- a/docs/en/Migration-Guides/OpenIddict-Mvc.md +++ b/docs/en/Migration-Guides/OpenIddict-Mvc.md @@ -111,7 +111,7 @@ Replace role scope to **roles** and add **UsePkce** and **SignoutScheme** option ## IdentityServer -This project is renamed to **AuthServer** after v6.0.0-rc1. You can also refactor and rename your project to *AuthServer* for easier updates in the future. +This project is renamed to **AuthServer** after v6.0.0. You can also refactor and rename your project to *AuthServer* for easier updates in the future. - In **MyApplication.IdentityServer.csproj** replace **project references**: diff --git a/docs/en/Migration-Guides/OpenIddict-Step-by-Step.md b/docs/en/Migration-Guides/OpenIddict-Step-by-Step.md index 2638895843..8aff29c84d 100644 --- a/docs/en/Migration-Guides/OpenIddict-Step-by-Step.md +++ b/docs/en/Migration-Guides/OpenIddict-Step-by-Step.md @@ -1,6 +1,6 @@ # Migrating from IdentityServer to OpenIddict Step by Step Guide -This guide provides layer-by-layer guidance for migrating your existing application to [OpenIddict](https://github.com/openiddict/openiddict-core) from IdentityServer. ABP startup templates use `OpenIddict` OpenId provider from v6.0.0-rc1 by default and `IdentityServer` projects are renamed to `AuthServer` in tiered/separated solutions. Since OpenIddict is only available with ABP v6.0, you will need to update your existing application in order to apply OpenIddict changes. +This guide provides layer-by-layer guidance for migrating your existing application to [OpenIddict](https://github.com/openiddict/openiddict-core) from IdentityServer. ABP startup templates use `OpenIddict` OpenId provider from v6.0.0 by default and `IdentityServer` projects are renamed to `AuthServer` in tiered/separated solutions. Since OpenIddict is only available with ABP v6.0, you will need to update your existing application in order to apply OpenIddict changes. ## History We are not removing Identity Server packages and we will continue to release new versions of IdentityServer-related NuGet/NPM packages. That means you won't have an issue while upgrading to v6.0 when the stable version releases. We will continue to fix bugs in our packages for a while. ABP 7.0 will be based on .NET 7. If Identity Server continues to work with .NET 7, we will also continue to ship NuGet packages for our IDS integration. diff --git a/modules/identity/src/Volo.Abp.Identity.Blazor/Pages/Identity/UserManagement.razor b/modules/identity/src/Volo.Abp.Identity.Blazor/Pages/Identity/UserManagement.razor index 486da65537..afa3fd3897 100644 --- a/modules/identity/src/Volo.Abp.Identity.Blazor/Pages/Identity/UserManagement.razor +++ b/modules/identity/src/Volo.Abp.Identity.Blazor/Pages/Identity/UserManagement.razor @@ -16,15 +16,17 @@ BreadcrumbItems="@BreadcrumbItems" Toolbar="@Toolbar" /> - - - - - @L["Search"] - - - - + + + + + + +
+ +
+
+ @* ************************* DATA GRID ************************* *@ @* ************************* CREATE MODAL ************************* *@ -@if ( HasCreatePermission ) +@if (HasCreatePermission) {
@L["NewUser"] - + @@ -62,7 +64,7 @@ @L["DisplayName:UserName"] * - + @@ -72,7 +74,7 @@ @L["DisplayName:Name"] - + @@ -82,7 +84,7 @@ @L["DisplayName:Surname"] - + @@ -101,7 +103,7 @@ - + @@ -109,7 +111,7 @@ @L["DisplayName:Email"] * - + @@ -119,7 +121,7 @@ @L["DisplayName:PhoneNumber"] - + @@ -130,15 +132,15 @@ @L["DisplayName:LockoutEnabled"] - + - @if ( NewUserRoles != null ) + @if (NewUserRoles != null) { - @foreach ( var role in NewUserRoles ) + @foreach (var role in NewUserRoles) { - + @role.Name } @@ -150,7 +152,7 @@ - +
@@ -158,18 +160,18 @@ } @* ************************* EDIT MODAL ************************* *@ -@if ( HasUpdatePermission ) +@if (HasUpdatePermission) {
@L["Edit"] - + - + @@ -183,7 +185,7 @@ @L["DisplayName:UserName"] * - + @@ -193,7 +195,7 @@ @L["DisplayName:Name"] - + @@ -203,7 +205,7 @@ @L["DisplayName:Surname"] - + @@ -222,7 +224,7 @@ - + @@ -230,7 +232,7 @@ @L["DisplayName:Email"] * - + @@ -240,7 +242,7 @@ @L["DisplayName:PhoneNumber"] - + @@ -251,15 +253,15 @@ @L["DisplayName:LockoutEnabled"] - + - @if ( EditUserRoles != null ) + @if (EditUserRoles != null) { - @foreach ( var role in EditUserRoles ) + @foreach (var role in EditUserRoles) { - + @role.Name } @@ -271,14 +273,14 @@ - +
} -@if ( HasManagePermissionsPermission ) +@if (HasManagePermissionsPermission) { - -} + +} \ No newline at end of file diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.EntityFrameworkCore/Volo/Abp/OpenIddict/Authorizations/EfCoreOpenIddictAuthorizationRepository.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.EntityFrameworkCore/Volo/Abp/OpenIddict/Authorizations/EfCoreOpenIddictAuthorizationRepository.cs index e9b4c4b3fb..9cd5397bb4 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.EntityFrameworkCore/Volo/Abp/OpenIddict/Authorizations/EfCoreOpenIddictAuthorizationRepository.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.EntityFrameworkCore/Volo/Abp/OpenIddict/Authorizations/EfCoreOpenIddictAuthorizationRepository.cs @@ -8,6 +8,7 @@ using OpenIddict.Abstractions; using Volo.Abp.Domain.Repositories.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore; using Volo.Abp.OpenIddict.EntityFrameworkCore; +using Volo.Abp.OpenIddict.Tokens; namespace Volo.Abp.OpenIddict.Authorizations; @@ -81,13 +82,13 @@ public class EfCoreOpenIddictAuthorizationRepository : EfCoreRepository> GetPruneListAsync(DateTime date, int count, CancellationToken cancellationToken = default) { - var tokenQueryable = (await GetDbContextAsync()).Tokens.AsQueryable(); - return await (await GetDbSetAsync()) - .Where(x => x.CreationDate < date) - .Where(x => x.Status != OpenIddictConstants.Statuses.Valid || - (x.Type == OpenIddictConstants.AuthorizationTypes.AdHoc && tokenQueryable.Any(t => t.AuthorizationId == x.Id))) - .OrderBy(x => x.Id) - .Take(count) - .ToListAsync(GetCancellationToken(cancellationToken)); + return await (from authorization in (await GetQueryableAsync()) + join token in (await GetDbContextAsync()).Set() + on authorization.Id equals token.AuthorizationId into authorizationTokens + from authorizationToken in authorizationTokens.DefaultIfEmpty() + where authorization.CreationDate < date + where authorization.Status != OpenIddictConstants.Statuses.Valid || + (authorization.Type == OpenIddictConstants.AuthorizationTypes.AdHoc && authorizationToken == null) + select authorization).OrderBy(x => x.Id).Take(count).ToListAsync(cancellationToken: cancellationToken); } } diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.MongoDB/Volo/Abp/OpenIddict/Authorizations/MongoOpenIddictAuthorizationRepository.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.MongoDB/Volo/Abp/OpenIddict/Authorizations/MongoOpenIddictAuthorizationRepository.cs index fe0042e54c..5103d009c1 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.MongoDB/Volo/Abp/OpenIddict/Authorizations/MongoOpenIddictAuthorizationRepository.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.MongoDB/Volo/Abp/OpenIddict/Authorizations/MongoOpenIddictAuthorizationRepository.cs @@ -67,16 +67,18 @@ public class MongoOpenIddictAuthorizationRepository : MongoDbRepository> GetPruneListAsync(DateTime date, int count, CancellationToken cancellationToken = default) { - var tokenQueryable = await GetMongoQueryableAsync(GetCancellationToken(cancellationToken)); - - var authorizations = await (await GetMongoQueryableAsync(GetCancellationToken(cancellationToken))) + var tokens = await (await GetMongoQueryableAsync(cancellationToken)) + .Where(x => x.AuthorizationId != null) + .Select(x => x.AuthorizationId.Value) + .ToListAsync(GetCancellationToken(cancellationToken)); + + + return await (await GetMongoQueryableAsync(cancellationToken)) .Where(x => x.CreationDate < date) .Where(x => x.Status != OpenIddictConstants.Statuses.Valid || - (x.Type == OpenIddictConstants.AuthorizationTypes.AdHoc)) + (x.Type == OpenIddictConstants.AuthorizationTypes.AdHoc && !tokens.Contains(x.Id))) .OrderBy(x => x.Id) .Take(count) - .ToListAsync(GetCancellationToken(cancellationToken)); - - return authorizations.Where(x => tokenQueryable.Any(t => t.AuthorizationId == x.Id)).ToList(); + .ToListAsync(cancellationToken: cancellationToken); } } diff --git a/npm/ng-packs/packages/core/src/lib/core.module.ts b/npm/ng-packs/packages/core/src/lib/core.module.ts index 4cf289a240..e3cd895022 100644 --- a/npm/ng-packs/packages/core/src/lib/core.module.ts +++ b/npm/ng-packs/packages/core/src/lib/core.module.ts @@ -33,8 +33,9 @@ import { getInitialData, localeInitializer } from './utils/initial-utils'; import { ShortDateTimePipe } from './pipes/short-date-time.pipe'; import { ShortTimePipe } from './pipes/short-time.pipe'; import { ShortDatePipe } from './pipes/short-date.pipe'; +import { QUEUE_MANAGER } from './tokens/queue.token'; +import { DefaultQueueManager } from './utils/queue'; import { IncludeLocalizationResourcesProvider } from './providers/include-localization-resources.provider'; -import { AuthGuard } from './abstracts/auth.guard'; /** * BaseCoreModule is the module that holds @@ -171,6 +172,10 @@ export class CoreModule { useValue: localizationContributor(options.localizations), deps: [LocalizationService], }, + { + provide: QUEUE_MANAGER, + useClass: DefaultQueueManager, + }, IncludeLocalizationResourcesProvider, ], }; diff --git a/npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts b/npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts index e06c6d404c..f277bf50ee 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/permission.directive.ts @@ -2,6 +2,7 @@ import { AfterViewInit, ChangeDetectorRef, Directive, + Inject, Input, OnChanges, OnDestroy, @@ -12,6 +13,8 @@ import { import { ReplaySubject, Subscription } from 'rxjs'; import { distinctUntilChanged, take } from 'rxjs/operators'; import { PermissionService } from '../services/permission.service'; +import { QUEUE_MANAGER } from '../tokens/queue.token'; +import { QueueManager } from '../utils/queue'; @Directive({ selector: '[abpPermission]', @@ -32,6 +35,7 @@ export class PermissionDirective implements OnDestroy, OnChanges, AfterViewInit private vcRef: ViewContainerRef, private permissionService: PermissionService, private cdRef: ChangeDetectorRef, + @Inject(QUEUE_MANAGER) public queue: QueueManager, ) {} private check() { @@ -66,7 +70,7 @@ export class PermissionDirective implements OnDestroy, OnChanges, AfterViewInit } ngAfterViewInit() { - this.cdrSubject.pipe(take(1)).subscribe(() => this.cdRef.detectChanges()); + this.cdrSubject.pipe(take(1)).subscribe(() => this.queue.add(() => this.cdRef.detectChanges())); this.rendered = true; } } diff --git a/npm/ng-packs/packages/core/src/lib/tests/initial-utils.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/initial-utils.spec.ts index b28447be99..90100d4613 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/initial-utils.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/initial-utils.spec.ts @@ -12,6 +12,7 @@ import { getInitialData, localeInitializer } from '../utils/initial-utils'; import * as environmentUtils from '../utils/environment-utils'; import * as multiTenancyUtils from '../utils/multi-tenancy-utils'; import { RestService } from '../services/rest.service'; +import { CHECK_AUTHENTICATION_STATE_FN_KEY } from '../tokens/check-authentication-state'; const environment = { oAuthConfig: { issuer: 'test' } }; @@ -41,6 +42,10 @@ describe('InitialUtils', () => { registerLocaleFn: () => Promise.resolve(), }, }, + { + provide: CHECK_AUTHENTICATION_STATE_FN_KEY, + useValue: () => {}, + }, ], }); @@ -51,6 +56,9 @@ describe('InitialUtils', () => { const environmentService = spectator.inject(EnvironmentService); const configStateService = spectator.inject(ConfigStateService); const sessionStateService = spectator.inject(SessionStateService); + //const checkAuthenticationState = spectator.inject(CHECK_AUTHENTICATION_STATE_FN_KEY); + + const authService = spectator.inject(AuthService); const parseTenantFromUrlSpy = jest.spyOn(multiTenancyUtils, 'parseTenantFromUrl'); const getRemoteEnvSpy = jest.spyOn(environmentUtils, 'getRemoteEnv'); @@ -65,7 +73,7 @@ describe('InitialUtils', () => { const configRefreshAppStateSpy = jest.spyOn(configStateService, 'refreshAppState'); configRefreshAppStateSpy.mockReturnValue(of(appConfigRes)); const sessionSetTenantSpy = jest.spyOn(sessionStateService, 'setTenant'); - + const authServiceInitSpy = jest.spyOn(authService, 'init'); const configStateGetOneSpy = jest.spyOn(configStateService, 'getOne'); configStateGetOneSpy.mockReturnValue(appConfigRes.currentTenant); @@ -79,6 +87,7 @@ describe('InitialUtils', () => { expect(configRefreshAppStateSpy).toHaveBeenCalled(); expect(environmentSetStateSpy).toHaveBeenCalledWith(environment); expect(sessionSetTenantSpy).toHaveBeenCalledWith(appConfigRes.currentTenant); + expect(authServiceInitSpy).toHaveBeenCalled(); }); }); diff --git a/npm/ng-packs/packages/core/src/lib/tokens/index.ts b/npm/ng-packs/packages/core/src/lib/tokens/index.ts index 971ac0fcbd..b9d724f9f6 100644 --- a/npm/ng-packs/packages/core/src/lib/tokens/index.ts +++ b/npm/ng-packs/packages/core/src/lib/tokens/index.ts @@ -5,6 +5,7 @@ export * from './localization.token'; export * from './lodaer-delay.token'; export * from './manage-profile.token'; export * from './options.token'; +export * from './queue.token'; export * from './tenant-key.token'; export * from './include-localization-resources.token'; export * from './pipe-to-login.token'; diff --git a/npm/ng-packs/packages/core/src/lib/tokens/queue.token.ts b/npm/ng-packs/packages/core/src/lib/tokens/queue.token.ts new file mode 100644 index 0000000000..66c0efa7d6 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tokens/queue.token.ts @@ -0,0 +1,4 @@ +import { QueueManager } from "../utils/queue"; +import { InjectionToken } from "@angular/core"; + +export const QUEUE_MANAGER = new InjectionToken("QUEUE_MANAGER"); diff --git a/npm/ng-packs/packages/core/src/lib/utils/index.ts b/npm/ng-packs/packages/core/src/lib/utils/index.ts index a50b6ef731..1aceceaaf9 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/index.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/index.ts @@ -14,6 +14,7 @@ export * from './localization-utils'; export * from './multi-tenancy-utils'; export * from './number-utils'; export * from './object-utils'; +export * from './queue'; export * from './route-utils'; export * from './string-utils'; export * from './tree-utils'; diff --git a/npm/ng-packs/packages/core/src/lib/utils/queue.ts b/npm/ng-packs/packages/core/src/lib/utils/queue.ts new file mode 100644 index 0000000000..33525d83e1 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/utils/queue.ts @@ -0,0 +1,48 @@ +export interface QueueManager { + add(fn: () => void): void; + init(interval: number, stackSize: number): void; +} + +export class DefaultQueueManager implements QueueManager { + private queue: Array<() => void> = []; + private isRunning = false; + private stack = 0; + private interval = 0; + private stackSize = 100; + + public init(interval: number, stackSize: number) { + this.interval = interval; + this.stackSize = stackSize; + } + + public add(fn: () => void) { + this.queue.push(fn); + this.run(); + } + + private run() { + if (this.isRunning) return; + this.stack++; + + this.isRunning = true; + + const fn = this.queue.shift(); + + if (!fn) { + this.isRunning = false; + return; + } + + fn(); + if (this.stack > this.stackSize) { + setTimeout(() => { + this.isRunning = false; + this.run(); + this.stack = 0; + }, this.interval); + } else { + this.isRunning = false; + this.run(); + } + } +} diff --git a/npm/ng-packs/packages/oauth/src/lib/tests/api.interceptor.spec.ts b/npm/ng-packs/packages/oauth/src/lib/tests/api.interceptor.spec.ts index 97fa7035dd..1b102b8589 100644 --- a/npm/ng-packs/packages/oauth/src/lib/tests/api.interceptor.spec.ts +++ b/npm/ng-packs/packages/oauth/src/lib/tests/api.interceptor.spec.ts @@ -3,8 +3,7 @@ import { SpyObject } from '@ngneat/spectator'; import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest'; import { OAuthService } from 'angular-oauth2-oidc'; import { Subject, timer } from 'rxjs'; -import { ApiInterceptor } from '../interceptors/api.interceptor'; -import { HttpWaitService, SessionStateService, TENANT_KEY } from '@abp/ng.core'; +import { ApiInterceptor, HttpWaitService, SessionStateService, TENANT_KEY } from '@abp/ng.core'; describe('ApiInterceptor', () => { let spectator: SpectatorService; diff --git a/npm/ng-packs/packages/oauth/src/lib/tests/initial-utils.spec.ts b/npm/ng-packs/packages/oauth/src/lib/tests/initial-utils.spec.ts index 488c14113b..cd2dfbcac5 100644 --- a/npm/ng-packs/packages/oauth/src/lib/tests/initial-utils.spec.ts +++ b/npm/ng-packs/packages/oauth/src/lib/tests/initial-utils.spec.ts @@ -9,10 +9,8 @@ import { ConfigStateService, AbpApplicationConfigurationService, SessionStateService, - ApplicationConfigurationDto, } from '@abp/ng.core'; import * as clearOAuthStorageDefault from '../utils/clear-o-auth-storage'; -import { of } from 'rxjs'; import { checkAccessToken } from '../utils/check-access-token'; const environment = { oAuthConfig: { issuer: 'test' } }; @@ -49,33 +47,6 @@ describe('InitialUtils', () => { beforeEach(() => (spectator = createComponent())); - describe('#getInitialData', () => { - let mockInjector; - let configStateService; - let authService; - beforeEach(() => { - mockInjector = { - get: spectator.inject, - }; - configStateService = spectator.inject(ConfigStateService); - authService = spectator.inject(AuthService); - }); - - test('should called configStateService.refreshAppState', async () => { - const configRefreshAppStateSpy = jest.spyOn(configStateService, 'refreshAppState'); - const appConfigRes = { - currentTenant: { id: 'test', name: 'testing' }, - } as ApplicationConfigurationDto; - - configRefreshAppStateSpy.mockReturnValue(of(appConfigRes)); - - // Todo: refactor it - // await initFactory(mockInjector)(); - - expect(configRefreshAppStateSpy).toHaveBeenCalled(); - }); - }); - describe('#checkAccessToken', () => { let injector; let injectorSpy;