From fc1ae26c27f02643c4312ce9eb472554377fba8b Mon Sep 17 00:00:00 2001 From: muhammedaltug Date: Wed, 9 Jun 2021 19:00:24 +0300 Subject: [PATCH 1/7] queryParams added to logout Func --- .../packages/core/src/lib/services/auth.service.ts | 4 ++-- .../core/src/lib/strategies/auth-flow.strategy.ts | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/services/auth.service.ts b/npm/ng-packs/packages/core/src/lib/services/auth.service.ts index 752721e41a..3a1dadc4cb 100644 --- a/npm/ng-packs/packages/core/src/lib/services/auth.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/auth.service.ts @@ -41,8 +41,8 @@ export class AuthService { .toPromise(); } - logout(): Observable { - return this.strategy.logout(); + logout(queryParams?: Params): Observable { + return this.strategy.logout(queryParams); } /** diff --git a/npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts b/npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts index 1d79311f9c..8594e44bca 100644 --- a/npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts +++ b/npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts @@ -40,7 +40,7 @@ export abstract class AuthFlowStrategy { abstract checkIfInternalAuth(): boolean; abstract navigateToLogin(queryParams?: Params): void; - abstract logout(): Observable; + abstract logout(queryParams?: Params): Observable; abstract login(params?: LoginParams): Observable; private catchError = err => this.store.dispatch(new RestOccurError(err)); @@ -115,8 +115,8 @@ export class AuthCodeFlowStrategy extends AuthFlowStrategy { return false; } - logout() { - return from(this.oAuthService.revokeTokenAndLogout()); + logout(queryParams?: Params) { + return from(this.oAuthService.revokeTokenAndLogout(queryParams)); } login() { @@ -203,10 +203,10 @@ export class AuthPasswordFlowStrategy extends AuthFlowStrategy { ); } - logout() { + logout(queryParams?: Params) { const router = this.injector.get(Router); - return from(this.oAuthService.revokeTokenAndLogout()).pipe( + return from(this.oAuthService.revokeTokenAndLogout(queryParams)).pipe( switchMap(() => this.appConfigService.get()), tap(res => { this.configState.setState(res); From 32de9da31155c77a484fe0b0f421d28e2b0fdb20 Mon Sep 17 00:00:00 2001 From: muhammedaltug Date: Fri, 11 Jun 2021 12:45:21 +0300 Subject: [PATCH 2/7] send culture and ui-culture parameters at logout --- .../core/src/lib/strategies/auth-flow.strategy.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts b/npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts index 8594e44bca..7c08da76a2 100644 --- a/npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts +++ b/npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts @@ -2,13 +2,7 @@ import { HttpHeaders } from '@angular/common/http'; import { Injector } from '@angular/core'; import { Params, Router } from '@angular/router'; import { Store } from '@ngxs/store'; -import { - AuthConfig, - OAuthErrorEvent, - OAuthInfoEvent, - OAuthService, - OAuthStorage, -} from 'angular-oauth2-oidc'; +import { AuthConfig, OAuthErrorEvent, OAuthInfoEvent, OAuthService, OAuthStorage } from 'angular-oauth2-oidc'; import { from, Observable, of } from 'rxjs'; import { filter, switchMap, tap } from 'rxjs/operators'; import { RestOccurError } from '../actions/rest.actions'; @@ -116,7 +110,9 @@ export class AuthCodeFlowStrategy extends AuthFlowStrategy { } logout(queryParams?: Params) { - return from(this.oAuthService.revokeTokenAndLogout(queryParams)); + const lang = this.sessionState.getLanguage(); + const culture = { culture: lang, 'ui-culture': lang }; + return from(this.oAuthService.revokeTokenAndLogout({ ...(lang && culture), ...queryParams })); } login() { From f4b3d4b80616e086b3b9e8436260373e29435811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 15 Jun 2021 14:25:32 +0300 Subject: [PATCH 3/7] Documented Feature Management Module --- docs/en/Modules/Feature-Management.md | 105 ++++++++++++++++++++- docs/en/Modules/Setting-Management.md | 33 ++++++- docs/en/images/features-module-opening.png | Bin 0 -> 25000 bytes 3 files changed, 135 insertions(+), 3 deletions(-) create mode 100644 docs/en/images/features-module-opening.png diff --git a/docs/en/Modules/Feature-Management.md b/docs/en/Modules/Feature-Management.md index 968b742092..9bbf344a01 100644 --- a/docs/en/Modules/Feature-Management.md +++ b/docs/en/Modules/Feature-Management.md @@ -1,5 +1,106 @@ # Feature Management Module -> This module implements the `IFeatureStore` to store and manage feature values in a database. See the [Features System document](../Features.md) to understand the features first. +The Feature Management module implements the `IFeatureManagementStore` interface defined by the [Feature System](../Features.md). + +> This document covers only the feature management module which persists feature values to a database. See [the features](../Features.md) document for more about the feature system. + +## How to Install + +This module comes as pre-installed (as NuGet/NPM packages) when you [create a new solution](https://abp.io/get-started) with the ABP Framework. You can continue to use it as package and get updates easily, or you can include its source code into your solution (see `get-source` [CLI](../CLI.md) command) to develop your custom module. + +### The Source Code + +The source code of this module can be accessed [here](https://github.com/abpframework/abp/tree/dev/modules/feature-management). The source code is licensed with [MIT](https://choosealicense.com/licenses/mit/), so you can freely use and customize it. + +## User Interface + +### Feature Management Dialog + +Feature management module provides a reusable dialog to manage features related to an object. For example, the [Tenant Management Module](Tenant-Management.md) uses it to manage permissions of tenants in the Tenant Management page. + +![features-module-opening](../images/features-module-opening.png) + +When you click *Actions* -> *Features* for a tenant, the feature management dialog is opened. An example screenshot from this dialog with two features defined: + +![features-modal](../images/features-modal.png) + +In this dialog, you can enable, disable or set values for the features for a tenant. + +## IFeatureManager + +`IFeatureManager` is the main service provided by this module. It is used to read and change the setting values for the tenants in a multi-tenant application. `IFeatureManager` is typically used by the *Feature Management Dialog*. However, you can inject it if you need to set a feature value. + +> If you just want to read feature values, use the `IFeatureChecker` as explained in the [Features document](../Features.md). + +**Example: Get/set a feature's value for a tenant** + +````csharp +using System; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.FeatureManagement; + +namespace Demo +{ + public class MyService : ITransientDependency + { + private readonly IFeatureManager _featureManager; + + public MyService(IFeatureManager featureManager) + { + _featureManager = featureManager; + } + + public async Task SetFeatureDemoAsync(Guid tenantId, string value) + { + await _featureManager + .SetForTenantAsync(tenantId, "Feature1", value); + + var currentValue = await _featureManager + .GetOrNullForTenantAsync("Feature1", tenantId); + } + } +} +```` + +## Feature Management Providers + +Features Management Module is extensible, just like the [features system](../Features.md). You can extend it by defining feature management providers. There are 3 pre-built feature management providers registered it the following order: + +* `DefaultValueFeatureManagementProvider`: Gets the value from the default value of the feature definition. It can not set the default value since default values are hard-coded on the feature definition. +* `EditionFeatureManagementProvider`: Gets or sets the feature values for an edition. Edition is a group of features assigned to tenants. Edition system has not implemented by the Tenant Management module. You can implement it yourself or purchase the ABP Commercial [SaaS Module](https://commercial.abp.io/modules/Volo.Saas) which implements it and also provides more SaaS features, like subscription and payment. +* `TenantFeatureManagementProvider`: Gets or sets the features values for tenants. + +`IFeatureManager` uses these providers on get/set methods. Typically, every feature management provider defines extension methods on the `IFeatureManager` service (like `SetForTenantAsync` defined by the tenant feature management provider). + +If you want to create your own provider, implement the `IFeatureManagementProvider` interface or inherit from the `FeatureManagementProvider` base class: + +````csharp +public class CustomFeatureProvider : FeatureManagementProvider +{ + public override string Name => "Custom"; + + public CustomFeatureProvider(IFeatureManagementStore store) + : base(store) + { + } +} +```` + +`FeatureManagementProvider` base class makes the default implementation (using the `IFeatureManagementStore`) for you. You can override base methods as you need. Every provider must have a unique name, which is `Custom` in this example (keep it short since it is saved to database for each feature value record). + +Once you create your provider class, you should register it using the `FeatureManagementOptions` [options class](../Options.md): + +````csharp +Configure(options => +{ + options.Providers.Add(); +}); +```` + +The order of the providers are important. Providers are executed in the reverse order. That means the `CustomSettingProvider` is executed first for this example. You can insert your provider in any order in the `Providers` list. + +## See Also + +* [Features](../Features.md) -TODO \ No newline at end of file diff --git a/docs/en/Modules/Setting-Management.md b/docs/en/Modules/Setting-Management.md index dff8da3f8e..9104daf8a6 100644 --- a/docs/en/Modules/Setting-Management.md +++ b/docs/en/Modules/Setting-Management.md @@ -83,4 +83,35 @@ Setting Management module is extensible, just like the [setting system](../Setti * `TenantSettingManagementProvider`: Gets or sets the setting value for a tenant. * `UserSettingManagementProvider`: Gets the setting value for a user. -`ISettingManager` uses the setting management providers on get/set methods. Typically, every setting management provider defines extension methods on the `ISettingManagement` service (like `SetForUserAsync` defined by the user setting management provider). \ No newline at end of file +`ISettingManager` uses the setting management providers on get/set methods. Typically, every setting management provider defines extension methods on the `ISettingManagement` service (like `SetForUserAsync` defined by the user setting management provider). + +If you want to create your own provider, implement the `ISettingManagementProvider` interface or inherit from the `SettingManagementProvider` base class: + +````csharp +public class CustomSettingProvider : SettingManagementProvider +{ + public override string Name => "Custom"; + + public CustomSettingProvider(ISettingManagementStore store) + : base(store) + { + } +} +```` + +`SettingManagementProvider` base class makes the default implementation (using the `ISettingManagementStore`) for you. You can override base methods as you need. Every provider must have a unique name, which is `Custom` in this example (keep it short since it is saved to database for each feature value record). + +Once you create your provider class, you should register it using the `SettingManagementOptions` [options class](../Options.md): + +````csharp +Configure(options => +{ + options.Providers.Add(); +}); +```` + +The order of the providers are important. Providers are executed in the reverse order. That means the `CustomSettingProvider` is executed first for this example. You can insert your provider in any order in the `Providers` list. + +## See Also + +* [Settings](../Settings.md) \ No newline at end of file diff --git a/docs/en/images/features-module-opening.png b/docs/en/images/features-module-opening.png new file mode 100644 index 0000000000000000000000000000000000000000..bd99604cd7fb0b34b48de1299bdd8920fbf9cb4f GIT binary patch literal 25000 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 Date: Tue, 15 Jun 2021 14:54:34 +0300 Subject: [PATCH 4/7] Permission Management Module document --- docs/en/Modules/Feature-Management.md | 2 +- docs/en/Modules/Permission-Management.md | 64 +++++++++++++++++- docs/en/images/permissions-module-dialog.png | Bin 0 -> 31723 bytes .../images/permissions-module-open-dialog.png | Bin 0 -> 25747 bytes 4 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 docs/en/images/permissions-module-dialog.png create mode 100644 docs/en/images/permissions-module-open-dialog.png diff --git a/docs/en/Modules/Feature-Management.md b/docs/en/Modules/Feature-Management.md index 9bbf344a01..471838e5cd 100644 --- a/docs/en/Modules/Feature-Management.md +++ b/docs/en/Modules/Feature-Management.md @@ -16,7 +16,7 @@ The source code of this module can be accessed [here](https://github.com/abpfram ### Feature Management Dialog -Feature management module provides a reusable dialog to manage features related to an object. For example, the [Tenant Management Module](Tenant-Management.md) uses it to manage permissions of tenants in the Tenant Management page. +Feature management module provides a reusable dialog to manage features related to an object. For example, the [Tenant Management Module](Tenant-Management.md) uses it to manage features of tenants in the Tenant Management page. ![features-module-opening](../images/features-module-opening.png) diff --git a/docs/en/Modules/Permission-Management.md b/docs/en/Modules/Permission-Management.md index ad48b60171..05426e9b4d 100644 --- a/docs/en/Modules/Permission-Management.md +++ b/docs/en/Modules/Permission-Management.md @@ -1,5 +1,65 @@ # Permission Management Module -This module implements the `IPermissionStore` to store and manage feature values in a database. See the [Authorization document](../Authorization.md) to understand the authorization and permission systems first. +This module implements the `IPermissionStore` to store and manage permissions values in a database. -TODO \ No newline at end of file +> This document covers only the permission management module which persists permission values to a database. See the [Authorization document](../Authorization.md) to understand the authorization and permission systems. + +## How to Install + +This module comes as pre-installed (as NuGet/NPM packages) when you [create a new solution](https://abp.io/get-started) with the ABP Framework. You can continue to use it as package and get updates easily, or you can include its source code into your solution (see `get-source` [CLI](../CLI.md) command) to develop your custom module. + +### The Source Code + +The source code of this module can be accessed [here](https://github.com/abpframework/abp/tree/dev/modules/permission-management). The source code is licensed with [MIT](https://choosealicense.com/licenses/mit/), so you can freely use and customize it. + +## User Interface + +### Permission Management Dialog + +Permission management module provides a reusable dialog to manage permissions related to an object. For example, the [Identity Module](Identity.md) uses it to manage permissions of users and roles. The following image shows Identity Module's Role Management page: + +![permissions-module-open-dialog](../images/permissions-module-open-dialog.png) + +When you click *Actions* -> *Permissions* for a role, the permission management dialog is opened. An example screenshot from this dialog: + +![permissions-module-dialog](../images/permissions-module-dialog.png) + +In this dialog, you can grant permissions for the selected role. The tabs in the left side represents main permission groups and the right side contains the permissions defined in the selected group. + +## IPermissionManager + +`IPermissionManager` is the main service provided by this module. It is used to read and change the permission values. `IPermissionManager` is typically used by the *Feature Management Dialog*. However, you can inject it if you need to set a permission value. + +> If you just want to read/check permission values for the current user, use the `IAuthorizationService` or the `[Authorize]` attribute as explained in the [Authorization document](../Authorization.md). + +**Example: Grant permissions to roles and users using the `IPermissionManager` service** + +````csharp +public class MyService : ITransientDependency +{ + private readonly IPermissionManager _permissionManager; + + public MyService(IPermissionManager permissionManager) + { + _permissionManager = permissionManager; + } + + public async Task GrantRolePermissionDemoAsync( + string roleName, string permission) + { + await _permissionManager + .SetForRoleAsync(roleName, permission, true); + } + + public async Task GrantUserPermissionDemoAsync( + Guid userId, string roleName, string permission) + { + await _permissionManager + .SetForUserAsync(userId, permission, true); + } +} +```` + +## See Also + +* [Authorization](../Authorization.md) \ No newline at end of file diff --git a/docs/en/images/permissions-module-dialog.png b/docs/en/images/permissions-module-dialog.png new file mode 100644 index 0000000000000000000000000000000000000000..6b190484a261a17c8738d0b0860be6aa2fca55b4 GIT binary patch literal 31723 zcmb5VbyQrzvo1P7fIyJofnb3^g1c*QC%C)2yM{n;hu|J8gS$fr?gV#tn87Fb+vMD{ z)?4@8-&^O-+JDTRp6;%$?yj!->g({&3XrHrl7t*LA;{41|?J!ia2Q)-6KWwY&6}u?nw0}zNtiH z7MQ7|2PWeaZ0Ze*f2W(H=8d!1Mk?mk)o6VbW2<-Gc-SnIGf4Iv(J%e;x*(9t{ExGrI!AAjlu|qI!~OFzoL_^F35CGS4xdQfsd|$6*?UawGG+>0tPUCpKXDnjJqaAP!W6#CNBW$uW{47d zJmn`ryI`F%Y&e}l7U#)JTZ*l=5s$`@mZR0!uG^A z7JHv;pL{dB-{&i_*=)de#Y1E{FJt?kE|zzL4khmH*6yBGpFE1E2yM%`h}#VoS}RwZ zyneSISf`vgzagIcFjyoZ)Z}=M{JoNDr*hFQC2POM`=T%UOET6w4o8o28uidF$35Q6TBBqZ(t}6IN9c$+8@1l>*wCBMrf{G-#8c>QVLZKaKkIe{N;URkT?=V)etH>r1aA10 zRz(XEN#(q_hZF^BXM}XQDa&BKD}0!=1ks|)&l}aFP4fE>KBg-@$-mn^AVdEVzm2(0 zXIO<^l`iB2Mk|D}7Olr$mEMdwmKF0@aS_n)~8(P5N1t|5X?fSd;oD$aG(SHhiSgnUR^E06e~H za~+W=6py+W76Gv?x0he>d&>e+1)yZ=QBMD)@xZbvwmVNrC}z|BYg2I996+#)IdhwT zBGj4cTGSuF&>r9gT{|k*AU45$|7^|B-%W_3wb1XXj5hwJMEExsV%cgrnBwB_4-dbX z-cUWuQoXG(jX##giO5x?YOy%68HlWOzp}j=(c>1?ZDJn9^tbEZu_h+d^8=Eh%Y%^9|#sy$29&0mTjWz!} zI}e5HUIGJE4Gn{K@2ieGNb6pim~cPqIB<3-FMl1-3q$tGMCq` zk%=#n(O2?ZN05fMVB7id6E_?&Ii74=eDtzl25P%4xf)-q6?d1niS3h2~B zOJ}Uj@@3+ixb56FzVzuE#WLIIEB!eRp8!|UdWo;H?Vw-#kNdZl3dffy@5_-0lqa{= zSq;*P8!s+WY%K^%dP*}Ut5ZZ1ywGSwCEpb@^Qp5G7k`Za*9_v_d3p~^#Z<^|NfK9Viq(;Ib%{oldNm$Pc&;deDGSh zDT-X}=N{`g2j8FXl6JitL|rtX(QUr-lN?#LvrvB;V8xW;}^tX1Rk z=aiy)AfV#{Ydp4Ww$H9w7COhX_8Hl6>n9XH2GEY(5}&DET{Nff*$}hs^$$!eT^9Xf zo3oyfvd?^k43 zN6u~YtLU$+k|f9dh^kF%*um{s=B(1H0kq9|#h{s7tVeoe$qLa;YppCD2dj=R-Kj%< z*1+J_ctN?%wpzbBU}HwRlssTkr2*S=X!36P3e%WbX4((kA7Su;1F7QDL%@@^0a~|Q zZj{T?xHlK&!;MTxLaC{!oxvL~({C(aZ0wwcxdK-HQwEpAs!6QC^$OGl*K}pp3N2@l7tN)e% z1}ypP6<7QDpxaqkq~rxY?H0k~VR>byLh;lBjjw87K)}@7JzcX*GAI@&Ba_wr;(;q~ zRt^b%B*Xnd_AP1g)WLm&D7V}FMRD)%o}ZlUlh5T0W(#MplFybDTIt%Yd0-(^2W_|h z8zN@+-5iy^n~P`V?K`@$rwwP~X7|94kz}vPbcXziIzJP3`oc;TG&aVjp4Oa!vcg@* z(5j8df{aZ9KrZUN_ILKpx5e03|5^&*Ctyy{*|2Uz<|27mYW$N(yVcow5Iz1Z`ps3S z4{H)21CI#tFFl3f;RTW5IX%2cUng&mYmrI^trE$H`vay9JWkz8x-mc@s-3n@yjj_m z4jYQF`_g`#i~dl9+r*WJgWH1@Oh(l{re(jO6-Kh;9wya32mV7V;xkxcCQNczZ9{kM zy6)a->MJnbwKcsDq}&4sfOY;>P+Yt>5Kr0aezI6^GiOuTe!Y`ouh{a!|wkU3yy+yw-R#p8c)#03S9>!cST1x8+z{(To21s@nYdQ}lWVOc1G zAxxct1e$I6HcM)>%)l>kJbX5}c0Ws27HIo>temjABD(sJLD2%ilGoZk1z%yYg;GN| z==Fot^-n2KImrqfB_ZMviFkUGon+Q)bP&nj2#9z`Tf7w-1ZiF(B+~^1^4r51l6oeQf2wu4m~2BR@AIu^;pN#o z=r*wP^vtFwjw^MeT5!Nqn}f{0hsF+0;M;kzT1=t^sLhYp!{7$=&jl*y^9jIHN_uO~ zgLI$nW8$8|qhG@i6fM6>Y$~dRTbI|ByGqz71!i;)nq^Z}IN!jpW3zS?J32&Ur0j-M zrVdwU@f{UM(QCKFsh9P)$(Jwg;>37l{1Ezax&UpOzFj?l3E9=oW;g#0aoayL*nr`r z)N?DeaOkGhpp~57pxp3~0AV3>$L%55W7TVKx!Gl^NFMNC{gKw!9#7T;Z?y&o#So)ikyOiXgbBf|7KRl!OhiGE`yWd!v~AU`x}t6v-8iNKc5GaCfDG5b3QN- zzjjOKiFGu$gEarpJc@4!9T*^!LLfT6m{BD7`C&cQ&Qn#yd=<)i;qQ@mm}Y=aQn}Fl z^kCNMLyrnFPIQSJa&EljOt#b3S=4Fx^-4lXMz z14gJQ^q^X*is5VdDUMX%bt#1x9ysSB?;X{@qF*6L(2kUiO3-oc(yy)5 z)gF;FP{afcShPr_@yYeg-G9F?nenqoxc7J+l?<3cYFO=9ZvdE#UsfSa-bzor@w2}4ZrQ~#rEYw zFX_M>iZn?Fm_qcer$!I`<$<=7Q^U&l)9+-xT1+pHy^yu(W}mt~YS5Z@udXicfUwyM zJH7blKMj7OBy{+tt1$gD2srjuJsSF|Y^4_{LWuQdYvVhY%Q(oGy$_`WT3ml4BUaA5 zQGd*3e`l4A=BR6J2mJ;anEj+EX!5CBcUqryhv~s{QkU?Sk(YIDw_BHnflVVGZu*+= zBE`SSl2M&sqn=0YA_MzkDF0CVfwkAaH@}&6Lku!Q3!LYTj5IWZ&}*H5p*unP=2y(ik>;~yZC-2 zvs$s*?u*1MT7P*~*Ccr4(-*NtrkmNoZ6PUswZvQaq)n07NDLO#O-igtHFmHQSXz9W zm0nv=c3ARm35r&`At`m3nVKT9<~6zO>KT(+%g7t8#w(zX!Oh)oof^OX7!!_8nwg@Wp$R(y z9~xd>RL~|V6Gx|7{;>KG$?WrZKGV;g2zqxn@q}9e;8sXW6*F4ApR~dF!4=%$;qkF= zy>Hj1+;gx-6d%p2cj_@3{Zcenp|;ECIW)A??0${!>yO7GCcSI*9Qzi0w0(0k<0){I z2$Y6>3;(|Z%>F|}_dkW(*3p`TPWAuEioeHv4t{CTRr3Xg4aAU>RookBf}Ih6Zx`!9 zt(5^6qn5$X;);PBA2J*OcR(2nh=>dVSK8a#3$UJRV8L|>z?N~i?-DkR;ejUeU*oZu zhF_3mZM{Z*F1~rgs}vcVA%Niom=mmcOboy0TK^FW{GW}iBriu!_*txiotfC*XI51x zZ+xbo6)A#?!|_juUa@H4^`jVz5+(4CswDUSUu?wIL6^fS;C~iy!z2_I=?YkMzuQSk zi$zBFEB}j=89D=bL*M5PJrb&><(nK7;zB4Ba4hPxgw{S^aV`B>&O|PN^Ig(%@V=1a z>ZUZo!G_*=zrbSG@B8kd1mjvhh8Y+-bAMlseTaR>G0*)EE#OTg9rWgh?!}HtK82`| zHRvI)H%&Mfy$#dKAH36=g>^pt-k6>DBs@TNy1mcUA_~lS;PxXuK%VrxQ4jh6jH)G3 z9zL+-4iARF?QoY*78Vu+_4~@skGy7GWpn z|L_Xhf-`)MDkI*8dVE$1m&s#W=%)r6q#%;Y3f8mQyrYT~fUa;_Y?2X}Xi&QszT4~~ z!)WoiyyOWSxQS%L+p{hscvST&@vpgm+{o^LI27p4+tW%d*?+ek;9G3qFl#b;p8Al$ z`O>a-O+;d3O8}63D^V zV}(~0jzGZIpxfS>S1t!xoK{tyuosm%cHBaBMJ292mLe%f{UBAg`#HnB3AqqgtIPxo z4d@M>;+%IZAd=8Y7B_2o5cX$%%3taI?$s|b3A`hJB0`48cVCeloxDnk6+A%zg+3^!Y(Y17 z2iZFKzQrTWYF3YC!s%qZ)gMpsO-uD+fD?A9_O|H88qQM% z=_OMYBlj(;=4TJ3)x4hRZvSspP4g(hk1M z@Y(wy^waC|mlP;&kW4DoC#T@e{q5GYf3anBYkG-q1g=xRcZQ+<9yml z^xtJheLmR@hA3tr*~qPU4+B*%e{i+Fu1w9A$2() z?x1M&(z*G)cF#7EyG1Hk(aU?_o-?=e-%vickffse@~RqRRo|4#uj+K~MAncRy!YQf zK5k1U4?5s^m3zG`E=+uzWHVGo6z1|Iusj$#;1h0{Z8{zljJ5OntHv9&%ETcuP4XY! z=d!cfs?*f!QjzBTyWS~H8vygD7>6 zppR~Y+TbyNrA4vo*ox0k+s+p~x6R*$_khijpGWM_b@s`fq~xp(bH&21kKuE{$+DyU z*$jY1$@p-!<^fP!t!6A5S1C~_D%EsrueDyKD4h8gx(2XZG762L&GiYTZZzWfHW@b| zoaNB_S{7~sUIW!yf=0)f zNA-5@E-kBLg`OM-fQqQSpg-Yc`V@UVexGF=j3=X3=C%gwbXr?<-mgi3Gg6&wlZSe} z7gibkcrEUx_8rZ0K5$f(xQXytO(RRw5at%zD?S@HMA1>63lGL;i%Er5pud%H=j2^N zh4!cW%Mbhdh8?xT>4=krXx@Su=%|$m2NR@RK?U3ZC%igI>9undgz`&Z7?n4tK8LdG zGc9yqWwuT%`At*tW9qUqo9kg+PgeI{RQs;z?{dDthAc>WZ{p%a$I437<6a_Q?P5gg zXE|ILO}a>qg9QHRFM+vTa3c6=Y;5fO{M=~fBY%=W>K=|9hU5eFk1;~^tHxNs`n#>; z`T*i4pvtZU-vy2OO9bq*xnUe~ZA|_Z{+*uq`^@s5B&!gZ4y=`$NwV7)alFX&Z~OC& zpJ%>xAocyfyx#U)U++V$MZjBABWi0CYBzAbUA_qr2jwqm?XOBcAGrIB{r`6d@Bel# zbKELYHvu72$(%HB*y5(|j-;`%DC9SCTzF zJul|{w)&zcUp^nKVIafX%Y!eqOf+Nvv%~*?li`2vI-sg?s`vx=X^CV)>8D(6;`*PSwbTq+g;J{j(AZD8H22q-wAq8h(h9h!djcw} zhR~E_`{iOORRAx(DPp|zm`NchS7TCt|1-(@maf`%maRoJ+n+e#KZtKFg$&jxMf&OYZ4Fx5BSjSxHJF!B z;pqsK*}?_xEW}D+s>!yN8a}w!zOih)YX8%=*qM|Xzz0p%wQD}o_9>g$LEODgI24U- zo#mPbpU;Tb|A}ZRzjA59nxVKcy89A`q>pU1q7Sh0U zH(0Ia9f)SLt7U&PB|MH5|@V9D3betjE4&%o;CqNOG(Dq7ooIALz)=Oy0mpd>jp z+`VQZ4$)U}0rxK6o*l5-!LM5i{9QA!zm~>5jC_^dc{?onVPq5?y?FQ>UmRZe?n?x$ z1x`4-wz4^I<0Q*UWCPs{9Vi|}zf&K6=y{XtxNHK%uYq&cL_gp%%O6u}K@|0!12IHD zQKH6E=DF@0e&*$ET*h}tNr4=v@BJUr+ng-}Ji3>cPah{4L@@SC7&x-r$mXD3QS2-F zTL-a%4Gmgl<>(I3nfIW5HlYB|uF*X~)@>+c5`{rmuSjJG)a8Aee5fytV4^fO3&>ht z4jeaySE{|cs)T{p=QWMXdNSq6T&Px4B>@J7--e!SjwTPknY+wDIX{9xvj|JSeAHp;O3b8PKGX9DNT4m?*g10R9d!Ptx`oM7@>M_mzClUiP zpfmt9RcL>k_3kamv3e!_x*L3}7LmG7c%jUB;&a9V>MD#KZwbI*BlSB|?ZuFH)~!PW zbr;}UaUPH&f%-$gyXPcc4ss=_g)#6Ow41C|;=_Sf@AgeRrcu0R9G*CtM6oE@gKX^= zzl$({x_letKT_RvJnp^mHK6jMxhTjt!2`P=FHu~+)Gm){s}Zu5iOImC(%8P=E$rq7 zb)9u)zP-_sIMVneX2SFWyMD4kGL~5&>YYE|BwtFL!_9jmeR|D5@B@=K!8Va3L;~hH zmyAUs7Wjt!yDA-c%Uaq;au^`bGQ+QbY$QdBs8?D`9!y+tKtFcOX@4vc6r!d@msZb! zeV1`~4ibzigF{L2S6%0QSkg@ogy_N$CMga%{n@l z#`T^XJn4AZ8^a%GFoS;*J1Vv(^bSyNG)-t5m@OMvds;`E7kckBV8VeIQhfKEMrX?W zH51sV8S%%RCn1c446$Dd!0nT|TKd|$NsZ3(3g=9;!Fm-EE`A1wZOiW~4<&c*&ZEq~ z(W!h#f$r`IX77gkZ3(>UPwjM)M>e569 zeY5RB&0%=ScFRQAiu)~bA}5#yWs%QqyVo6JO9nE8@UMmIZk!%O99rTOd2-q6{9N2E zW(+M@JGux;J+rR=VXLQnaKR8Wl)g(6`(rS$mZJy*G}x(|%)b2#wC#M*+sed0YjaU2 z#WR7*Ubh5#_vOfjVeNi+6O*6t8@>9w*t*5L`z+H>BO`g6`<@mx zX$K9L*EBZa*ZN3Kpu;oag>mC?_R)Y+Ao(O#=bs)C z1ma^a0Os9|p(d;I$2{ zzqlU+`r(S;651qA{yAn=2VvqJiM%0n@*@ z3n<#^(TuoCz#1|6kOwCx3)iag9mz19)a_e*&i=f#bO-{kXYdg6u6598f~vVR&8!r> z{&`EPbJo)`%yl!QiCs+;Dp-Z+L+`18m;qC&GI1|W1$&wKyR*o;yscZ&)lcYYTYZ_h za%55zhk-GJ;ctww88B_fz>P#M{U@828y!^DZ*X%N8i-HKhp?oqnBXlL9)-^red|qp z3Q0JC@b2lUKME5~fPkndW#^lwOOHU2UI+N5U2rNqwxLK7r%#}kY&?qayeq!d^){La zIZ#{&O~-k;z<0M>-KUBKRLAFTC2(8UAe_pF zZ_9^761T)C<#T!C<4hw|g)Q`Lh;XAjTEnf+6V8)AS`iq8Xd-}0s>vTvs#EVue${}# z9HKcAoo31qr-~+MJ{dJ@HV3#{q6+jHN=B%kpSd_uv^7lNIO-*Ai|EF{WhK~5=D-Ec z7bXl(M_kR#!|~}`!v%6^ZR%GNa|pLK|1kBT_(C#pe(OmJ`eu(T#4>E;Y~X{P`3-Kx zsv^^EwCvNO3pvlMRDKJBIGsue&%VT#PRb8H;wy)A-nQYsQqmX%kc(M8UY>-m@_I>` zRf%758A6=h1Y-`%r4yw^Eh?D4z)XezEfYuPUq{m!H@7ga?{d3@R*us-Kd))=+R4@a zrm(Kx{q!ra73rvq19nTikP=KBX{_T%<%FKxIz&<1AcDCa?9qRP>=O$_fTa%5MJXv> z&#_nSVhqL)B7deXZ^)5{;%Sfr|7vaaN7D{U!s;{6w{!oneCBfM{!UC}Rh;B1P&R4P z>NrR=cbpTgmEeMC%k`N_MT6|8T z)#|@i)}m`_K5K-==SB~ieCaf?uDYFRPFEZWl>0c*Ij!%QyJpm=F3{;@aqVnSKz8b7qt&a|3cF+ZX+VwjX#_$RkY?x}n7dLCPzYpl;3hHEp^S6oF&C&YlskK^V1Twu8H z2;aoo5iJ{y-bA|ReiCE4n~97ShYOs!yl*iZw6YppSlk0|chXE@?qkOcKFC{DzzaM0 zS^Z*lOII#c_GMz=(YcV5RtwEpDo1j<4z1MyoTJd{)5*HJIyW_gs3yk*UJpjc(&htf z=0?M-z*e;=z{NMP4Drb$yr?ChONyLQ$DOf&tmcs#`O(-k7J&i_+#9J+d}zhbYw82K z9MC!xi&BItp0ZB;+%yPUJ1af*+L;;GQiO2s|9i}s9?0WHgH3r;pmN<2o*pYG!Y=q` z?@*b@SU#VaraR+(Wg>6ROHWl{WBugqfR4pn-O>bR2D$Xi-WKh??ZZqTHC9Om;a2qD zZ|V@xX?<6Ju028<12P(Qu@FJ=EHtqW0>1h5Ft~t_J@d{}=*h8@3Oha`iuacgUnJ_NK_jzHU}hUc~Xnm&G@ zQb!WT&uBBMJlOx9mlMSNG344&Eu2vY3mv3{vJeugn6$(A=Cf@Hl%kc2T{cPuvWJVDvoYToP$^S$)8wa zL{Ul`33Oj}iwl-NPrN|CX9UfqBLx`1h2&xsTu_x{E-+Q|}3%FnD^lOnsWh>vzdQRdVU%=CMMay>+ff(MLR*^g% zj)S_=LsJV^@EK=27SKSkJ7rCG9aS;0imxJ3uy zLN9uBQ;bbIN#aKHZgu6es)Z9a5V#cOw=2#?c0?BXzCJbq-9Lbw{LOWJ=!?J*{@#3d zz{e+Bt0rKes;xD(IJvSk1w4Ldwllx3c*OhfK;&kT-?fOaqVqoou)?96hmW(f^I$A9 z!XO%Xj3~0&%y(CTV4UEYnm>|3%2L*got{Aa^pyg7>fi#oY+IZ>+x<1SO3Xb?u9Kv3 zi(KA4jwL2W*(-LUu)ZfPpU3ck!W~M#?QIMf!kdQXNX|+vs#8};ZZS#H$?qt!$mG5w zt`VU5==b%KH3LB@pPGbAxU z2@l$(m-^&_bDDMFao_EokKzL4Q^PU70Axz7)A2WN2#xmHt|g!BE@JyG`Ncfis~`L2 zfOJE4SR=h382WTv9-)%sHL8STQGOM0$B=XNjE}ItyI(oIK1VJr+I5Nze(tADtV~^e zGu>5oZ6P)t&hSxrg?45JYxJj8ThvPNsXt#wGY{=2?C5eIY7;GZcSdnqU6d2Eq1;xI1crjl9jpfQxE_CD>Yr=cQsQ!({zWc z!rsE*=YrF!DDV0jkOvz9yZ(+;DisryQy*}B6=v<*`G7AX;9_1COSsw91 z75tPjWOL!SiN7kF1q{?O|L@U1bSM9H*Z&>jgoQ)WH9?Q`@e7^$2seBBO!hRT%i+F; z?6W?#MaY$Of*?7Dyc3HrW~$HltcFxUsiYkG7C*k|8u*R!2~)Ec-_o4_Bt7#sRUlc* z0a9E_mQ6O_1XFUCK*ex7-Tl1mddPuv?VFDbH;zQ>^qIM*5HWm&Or%U*R?9DmX~sd> zY6K)s{ZE!qqk(Aef>`ze_7=mgoW1ao#L2jcDUcw^3gAClMGI5I4nG z*yMVUFdpMQ9=_rvsygG%<}fVqIb1RjDE>;UfKZ`1SsI8PNMcl||KW-xFfnRrw%4eD z*ipXNC1;uqUBOu|NaM~sE|74bU<=$kc|!_j*$|L$%#h-mxAB01~mN-3(69PY>H?xyImT~I3WhovAu2yFeIzvv+%CesGIs?K}BZK4-blYCkORz-%{)JoK|i7DVa)I z-rdh*{HNfL4+ie0YM)nqvSZR65#k56Y&vJMeK+2BQX20!7xc(16jL&dQ_(Bvu5?>Z z1#TObut$(UnPkj-cCsB$nSXsNKhQoyu>Z?}zQl7D%{l94m20&Lx?!o4;f?5NS2;_7 zV`}E~E?;&X_4?x`bsH*?iy@yjkV#u4CIV9&%|T{ZU4#QwZJ;u*ti0X6-8W}>k>0$M zouFnBs*K&{`d(>hMZH_;4l{hK8NQlK0i)RTHidl>E0pAD;s!^~BWLAm@(6cpVx6(u z^pl6m@-HGLli}8qySMh&cTCJY#Ilh7$Nsn>lqA5Qfx`w$Rp5`aX*2lk^In?YwMzbe z?NcDZ-9f1N9dQho991XSY5d6Z!}?~=yR7L*E!9!Jfgq@F{V&@=n9vpam!+W@vmKMB zMoPkQBxGGWG$cao_C$7liQ8YFRM>xw`ezEpUVbfE>(Px4+aF_5YB)1Q;d{LA_`Elv|IF(|Le^hk_RTsnzI7&Es$3g`@ZX5OQy3*vXbbbcBpz&R54l39`|3F zR)jSitjW(Jj7q~bVGf)`>+DkIt+FtMURfk=y-Gv-%KE2>AKL9X81IZwlbAoxpQQIP z(bJ>~{`BJqw$K#FV(2s`z2Y^KdU^wrJy;`z#>QDu^rro^OJaGwyNXG7?#L&ME;b6L zpF$o}cYQ1UB1A7s1L?R)VCKP&{S{X-0L4NGrpPJs`6LIZthsmV19NvtoXp!TDs39> z9Km=Nt(YPjhkg`B1vb)OEXI}@K1)*saO{I8l3Vb3LFMwu$ zpHx)SuOq`Mq-7yTy`Uo}tj%DdzY84mKbMvi;#RCa;X;5wcedNouJ&bRfeBx8WivN` z5OWt51KEMts*|Ng3R)mrA?myP(qVG!4^6FpBx>9PgvYdL`WtArWb4!B&g(T+rcz)qu3!Ecl0;+_1+X%HxIcTj=8h5ja65q~bH<8}0GKg+Z(a*?sg z>qZG2NEyE2%*jgmW{;tsE~iR|k5t_cuQ-v>UI;61qDH3+kivA;*b)@aZy^3?P^^sh zr;?f^*sT98)@1ps<|UWlmj|0evqcl_YCX0g4hFIxC66etBFW%*j9DY62F}votX0e7$B2t(y!rmpcCWR2Q)2J5Ey>79 ziv0m_2jrtM6uCzDu2P)hX1Sbwy3WNZaB$KaT{cRJ+{M!$Y)L zBBtV+bNV|NPv7P9g>TFew}nKr)<*qDjdu9J6J5UZY|>dV_AjGJm8)7ux2~ba!P>xT`el}B3%U7&1sX9MqT$=Yn+zFMo8cLzu&?E#z2S@T(;|Qi zLVWwaC2>UdbHPI`BRTautep?YI>O`;I=K~ou)3VLb3KewFRLVeb?)Nq#6^9_#+v+S z2xMvCs6%6wjWS9#Z==5SMcA2NMMxThewgm$=^kl$AE;o1$OGQ<{cgL+;a|##?Cs4P zRD`IdRMM5;w$8TD$_69mLBt%%EA`~57IW>R0KW~+X+O~tp1zr^cpS695p=Rth%NRcp3L>4U9nj=p0%pMelfGo=m(dpOH$A)x-5{%Hpa1TuiS!kS4{ zc3Ilhe(O!vaur4}6b-c&N(nulP{Hspd$l*yt?(zE?h6Z^j-DK=(p+tYt>b@}PdQUV zYhF=@UgRAE);0;HW8*|1QL-^ z$rO$26Ap-0I3a0KGcx!6iNvPB-HZuVYOtWn-umd3i4qIo!cFS+DCRfNqSLUpIUwaI z0G<{w*GOo7eV?ell4!o+!rJ)94T^NgZ+MTi-`n3Fwr5K6oOwjq$tYon60smSY;k3)A zT!{$pKcp2`u)@;;2s`RJ9OEm{rKPGknx5(LOlJ%g!{*)ZkclB=hU-7{so&v2^e@2@ z7VnJV28>vjl|g;VWuaisZ~BmwYO^Eqv zWuV0H&Tz^H-WPxvh`>b@!l~O2f=b{35?N4joYjI+9Rf}~RT-Rq{W?#wMdS(YWmh^% zm|2uol|O!_ES#Yt4SNXlpb=llLg7AruykZq;)Ec}4064hfW3BMT- zekO=f#9ZIDT3fly_!yK{FV3vv?Ajq9{`be%mlA6u zUG6f+<}O6JJYNOyEng|61aoLP62;ZxDlm{s7XJcc%2jQnWg>CE`jTi|-={20H>++! z{66MA@#8YiH$WrOr~KKgn?FA(GX!4Ea2p0rfkY!yWf!z_X@aLf#rWO@sqaxpG`jmz z6fnG_jk9umFfjSHbdvY9QH^M?-Wv&OFxXYN9+)i})ljM+f*QuF&A(eHwh`~0fa22L zcL0~V^270}AFw0?S2&kOSuj5#TD)T~)MQG_y*`vCX2gYA2nLVlARu7Z{F+Aj?e1(D zJn}o{O1Svwbg!;Mz`{Na)2P8_x^fo{!v=0{gs0?`n17~)WHw@Iku~x3&t4#EwQQan z2?g37Do>6IBdI_6s|>7tqF#G`JT&39?FTQuKZSFfEnTji8YG5)HDf$MT{q)!R{5N- z_VgUmzfG+JE16w_+CPS9T^s#*dF|C3e^|IxF7qIeB9bnLj3CtdYM}hZYsEzpIQ-&j zgr7ppIjCXxjW-szMVkArnayXrRxCrb#&4GXrRfTz`H+4toHm(zp-YNYeT zYfXAytLb*H`P$vQ>#tSZGbwd{c6)EHzYmGci}Ynk-nf%l|8C+$l%-YoewCqgq^Cy| zN6Guz8j1iuxjbTI6{{^7(a%Y(HETp1QWJd2e@qh3|jy(Dq zgrc5FX%b3eYgcdK+zmXU9{i^JR$Y+h^d_iZ)RF;hI5$I@2m!umL@EmdZ5PC$140u0 z&#=D)0kA(>rl0hRnae-Arajqzu%Z8f)rnUPre1%v;pP_n@_NPW{}_buTKG~@0{8QN zk#gjtO#Gk7m0rZpfB4)j7HtV{>&5jsg4*q$m;d!85sU^K#u5Fg7uXyTkciM4oRIXE z&S>q8BJ7z(=Ru1r$T#O`UsHhls1#-tOEmf7<#HCM-yt(9Lf{zS8EY=l&vIV8nolcY zTX0|zL=DV}7h{hB2(KaEw->md=>5}JYYI%9(OcO@W@9F!ff=HHiNlc}z6TMJ&5l-i z%i>4Nx+$qWM`crfY4Yrp%J?GgTh#)*JLePo|K zS92-u$JS<`;vS8)8{$idGf3ii6y1z6*cSP^U7^<6oYu+nX;yr5J_)CPzMLtLpwS9A zrK*iWJC7wEdy4V_b%G2e5y{i)t$GZz6Xw%t9*>E1?X?*{ah9D9X=huUsh@q~yC8sf zh08TX3u3s89tOAsb?lZGB7 zXySgzy%r*+WnZRATR2c6+8ZvhR*>=7gyVx=Vf??m=m3NGu_j79P+U4JXmPLLrUzNC z0;icRHMKCdbkcoHunOtDP_u^K9mL8gMYnba$3mU7XHsVad%2zp$_yQIoH6A}PWG`D z9b)=N3~Wy5MVrGL$ouP&w0Cem&kH-tkPM%d&OMubh^ct(#U&kbBK z#yK8xGfe_|-W|h=^+ZkZNcCt057P#09EiV4fP|U#liy5KEy&#*X4Uf+)H4u@+;Uv{ ztpl{!`kliSbUQO=Q1l({Q4p_MpMEX)&N0}&9f=~JWyfOQMUBY4wA%FE^?z^g0dmxqX@|9FEB zYmcLM*zaYjHt{_r6eM?NZOAEzYyeo2yTr5#;nfy31#y1s?`_95291hm$DBqSB7OT` z&iqALye;kIcd&;LPwpN)6&>b|_PDkb5E8TmyQFSo(g)r>l5|5Lh%zD8awzw1G?DJA9YT{~X({8Xgd| zSB#w#V1iHWg(`V+C*&-H6_zS0eSgqvsLTcivDaH?R7qOYeZM=}m-(+qA=#!z9ayTP zRSE|xOG;VRcE*UH5)ZN!a0Q9;KAZZ*=Bl+urk7;wc5>@CL6r!4$&H`rUXr7&OEWW& z0*tiyO84;!l}2!}R0Yp;cCMlOx(tZNEZN8SjbKPL)N1)5j2my&$h%6Il zO)tepG+M3jHp}s~KT=H1o8?M#bZcl)*TYWlyt#Y!^miM5pm#6hu2S%gRk_A&e1Qkd z@9lS1RohIyo9*E7qd7?OW5lU$D9S-|%sl2)5`AwdxiGj+4y-Y_Y;7V&-*H4tvB$>x zSz3*Sr$#5jLx_1wKO~#vK5gVKjO@nwmAto`Z{}r?Ix^Qmrr)+Ps=d7^9xg0`+%g0c z?pGY-)xR0bw7Seqvzn_JNu8+_C*e(%5f);2cDSJ^xKhi_J#_I61q;*`EvTcCqGFBv zN4w*>+!n@RpKAY*+rsA>oBV+zB2M2xcF^aEgb;@ZPNiL%p236SGI(V70Qp{^OEVy= zyAfLQx0#Q?Rvdfy)qLhx=gR!#2F*QAoll9n;C1Z1N0r5;&n4Q1!_b1KhUpe(9=e5k zY}u^OijGWB-eH;Ap_Y*Dnrl0|VgY=;qU7hMLQo8<6Jmc5^^WVuDfdSj>dkg;L!zIY~ z3yEHGDGZtz*7Z9-VQ#Pa@_;9xFj9zce~uXApkdAa1ommgn%t9eCicdq#7yQ6oPq3& z)Wj)B>-DC(T1+fLB_*GCua<1p4)WK=_#v@UHz`IM{2_%KDJ`5!ev+NN$LM{TOSJT2 zRqibsHul6XERRmxhZZY}--iqbT&JDU_zs93?twCp8`td|T)Ct+2K2ECam@c{`#byxfjShL&8G+%B4Y&}f~66nC!WlTrH9C`5jww7OmP)I~aR7ri&pX`tx| zD$gLRy2ehf$63?I`&>rwSs&EHprj4y9#PhW?&~J^qa0+9V;#5yj2%C9B};yp%uiGh zOh3ABK^gz7jHJdQGLi7i_xhQ|`-66OfghNwE?dh3)H3r|OZ6!N0vNgxp8I~7T=P3K z(VMuvYN(K)FqK#oWzmst!sWnpN~TU&>@4=L;`v7y^=?5_oE^(GtXgB(3lLOU0_qT= zU@)3z>Mc*rZ`pmE!suo2BCJ%}!a2-h-xaVCtlf)$I^9m)_XL9UqLsF}&2Jqx&Cb!j zsvD1;(s)fYH1-(g6-J1b!PtuTSWNGfj;1bGkB9k++5*O;0bZi<*tdF2Uy#1B|} zO%Li)3p<~r!LVOG(7x^O@1ctGsq^GU6*swyV6ter6>PqyId(dXl*+p4{7<-ZT87V?^>QrsF?YZGs0+Xz^y05H5J$0lQif%(N z-_K}M-1JgLOP@D5sh&?;nC00!%2ZCg40)W(RYCmm7=7LU`vR_nS?W=r>VJyP| z{Bxdr!#D|MHdaw?lRel82R*P3{YaghLFY*kD);bGXFGk%6(L4{49!$q%`JN)TPWDLEEP-Ze7BAyacygVO)uLe;~|c|P1`xU8P|=Ure~Cy zJ1oEF4!e=22F7E44P2U4AYbLY>_y$Wby2gY*nig>-`2*m5g~f;E{)OZ)1uv8#$MM!hGnoYKKdX(u{OP1h8s zR>4V>o*?TQq(|pN9M+OQ6=q(dF2m-+eHvA+pv7dU^njJ>f|qSDY=37ULlF$=YH2(g zJYN!bS5q@5b*7fd?gk`?_evTv;?h->dY%y=zI#O6;Epj_h-N<{FZN>Xe2@A=i7E33a?7|qhBJPu>7EbfNX5;!evu0^MHni-|KCz=58B1~*;}z)N zHeeNZFFciLv7TCargfRe^1D!zlSlys`G3rid)Q?;$!KZvZZ|F4#{c}btJet!B2C;$vq)o{e$WeJ+#b78gr0W~wpS6b!t&kaAn*4aTwH{Nw60E1q*4T7 ztgy~1f^i)J3yr68l#^ZJW>DCnom=lTz6GL;cWDqRV}%7e%4H@%P%f{@5ZE40Iq)+Y z1btS#&H?_2rud2iL0|D+U_+2##w~Mjh+ae<6@ui!K!CL?iTk&gR7r7pR7heucQi~G z@m_9%lOO7Je43!?pP|+XzsY5sN(K&HXT&j|#1P5vJXg!eWv9FZhZ=2c<=x)Yy-JSU z<*u>~b?2fa19ue^Cb(H9qe!v$OC&tV?i6O}`bECdc2qmdCnb0UM)@32IO+Z1$N?Yg z-mZ83%I@iH1GyGtN5G$`lOu7*hSc>jpPJ8Hew(T!cG@mQT+*IS)(p1f3Y#j~l)+h7Y=>Kh*u!r6@mQVK%&WpqZqTV?kDrSNvl-Yxt1+f`zz;VhOEI zHEX*(YgPya3KD)hLhEg*piN_g2j%6e25#*d;Wf;;5R==2w4u&bf#nfi*YBR4CcYxNuJR)qC#l}u@Iq*!Qnq~#J!5h!Oi**KS*5YLvp6e1lfwYjI6%t4H zjmwm?O5ZuZWWlLiD9n z;KiN2Wn&PhBJ}I>wptYa zR1JSm9SWrAkL)Cd6m7nJi}SSGd?$Hr_2CIQ0ne63@=h^hcuhU6X0EI#HL#&#LL{x*p zWq5c37Xf0+_qHL{n}fCWZhft9ZkDtbm@$J7pobq~6;O-2FD?jKmj+%gE}5q$HJ3NC zuQ+j|dimm$1*(oNi@!*qpwN-Z88O<;{7MYvz63b+BHB;8+^|znd)p|J2L@gZ12jRo{JcbQM(SI`X2) zuX(UlIeQsC@L8 zL92Ww$wFf2;q@2Tf|YZ77>Jw>HJ*jB{s{`i(S!#LYeUUjm7!hHtOL?KmZLf?-baKK zU(6wI`B#VN4--vYCDWS_O(v=belL&X34)Uo>;i^!U-P`=;uN7HsQ}GlA)A%5&MDqs zG{6n@XAiENz}gLYf(=079$k_=2PAk$Dg}Be;n!3^~dtyu(yW ztslCs@xrXT61R=}VRl7TERuJJA?S+$a;vG zH}SJs^CFgZJbNjRn6PdZ$xZ_ocC|%}@{{r^KjF#Gx1-F6ZEv6vC4|sPCRq!D&a=0A z30i$*7edN?*YJ2_!_v#kt2!R#j_%Hv4KPjGJRhHL^PhzI6{C7j{DpnS5`l$}H&x?# zUJ<;UW#W1K-LF6SPDX$3xcplIIxsuQz|->IIL@Cjzc9(H0fFrqGYyC`e>Y_P3sLUx zTw?bW4tb|*pKh$3X2*pt^T(wiDCVGGlOE7hXDZ;9qB{C~Lt-r!9MhTPsd`CD-0R4tch*8<<7Xe zu@lgk1M!$jx_G>cdNw7GVw+q`N<69KQ3^EymJfIRNqxhw@-lMWUTt#{#xAj8!0I3t&n33~TnHusj zmTwUed17cUVAR9hXHv)6Xdg(GAO?k=mmZ6}9z)pj+y9`se7T?eTA(TQ-ucVW0q4md zUc;{il%r+{8rOv7QB@hvV>RNJ-=LM-v<#Cn!q*Ysb z3H`x=i~an*-B5!R(O1lkJwOzncZywzmC15AXcC6RZH#bqhS|i}3y*5xb9}}cT5UCV z7qQeH_;OuQp&MgKbf)5&hSp;WigF==$QWO;wS$QiGg=rsX=i^9=>Yawpxi;!xq!lI z?tP4lTh$piWt`RtLhL51Gl`objTgx2tu>$=%oo_|EqI%mW2n-;ewPyYdsPowp1Zcj zcw%i@p&|U6Xr1Zi;&sGDBuBfhc$L)~DkQoMac~hEaQU6Doj=O+*BWtky3qHVZU+3i zsXbg?G#JiOp&T-PO(f7l7pD|2%V*#)k+rs6VTRX0|8hFB#pV**FFwIe!tr#}!zR#t zaxF-NcTg!++42B!>u@uOmZlDN&m_YlwN4S{K(g)^s7BCe4rs6{Yr?rf_vLqoV&yjy zrQd!Klnbdnc%=0K%OP?RF=5qR62KNo$YaN-Rsm{V3ms$FAI zVp!<#b;|dEXgO%g_*$!FoQ$xG#Y{>$hB6g0#}#1|-3R1|vmm2PGK&ykN*R$b)~2lU zIS@|$Yr@jLTeAz}d%5x&kOqjCdS&#}I%#7#b>Roy!N(i191aRB3aH!f6J=OprbZgO zWEOu}_@LX@@;uB;`e>{yE~&7I4rUDA5fbEOkLBOr7|dXD=?l@A&*x}kz|<>y^c6)z zG)=>PeZ-xD(gq$s`r6=vOGn-20ec+D4;H3G63)XGoSWZoh~eUfE}n`=rQrU-O&+Pc z6xZnMeVLu-$q~v|z7w|*QoX|^Pq;BMt!7Fvs1z-sT8VA{qfUz1dPlw5kiY!P*YaT_ z%&P7tLl^#c7MAlc&`Drv8N~s_)va9U2e`1+4k9xN#&#%o`d(p|#$ZmtfFl~0%JG$8`_pR6cpJOI)P3$GHdD&(RwdJs<}QQmzf882$7t<)LorE9w>k-*LnSpMT!huee) zH$!@`UB@|EcH7O6*zSzwp?GSOm8~P17X`x4)#w}LiS1R(&o?02`leb8#rraGuisNQ z4gvFIEy8}GJ^Vfp@uuhJ=Yzvo-X}h{jc00$o*fB|djW)A^LShXM)T9~m@HL<_Qi(B z1lpq-sNIQ)i76u^10Bai6d)rKv#+e?0lLk2?C6)$3Y)Q8aN(yN8puh#G`zcfyM80>90RvhWvZS??3+Z4a)Uk;ZLZYcv;> zM;*#27B<1@v=+)d2Turr%WG%xG>roirzvloDr&>3*8l;K-A1e@VOb_^;8?S3)V|9D zjz_ukK!48Y8U!yVrGD+JATj(mcq65d@N@Is zn=)u_>*apI8|8kUdFlcp?PBLzb^z|NQ7KRzrOvKOn#6&(r`Z zzNNW0>*hIr`!ZPlQzn2F;l8^1&@#FvU#U;=qLar zmzeDo_6s!c_~x5Wj;+p;Mgt(WHT&W{`>r+9ursxy+`0EmDdr1btR6(V)dxTm|a( z*u2`EjCEr#O{U*`mvyNRN+$yT3fsmwSBrT>Tc8NlFrAG1sYLc5w({|q&r>79EGvU@ zvz{;WQSq}<+Aa!Bi#c5l53FK9%qP6KWo$~}_ZF%5${TBFaB=$%qS!Zs?vl>Chrhws zqL99mxswGXESOzv8=ctMoI%4>kuge11H!^cLhK5nCfx)MdTZIwDVQsW+XH4C*wxzg z=(Qu?PAYs7KunE~&PN{&pujCUHY2%v_I1%KJo=Ij(sKd4lC3Xh*qR{&&{nES*Og`N z{ScXK7hZOEyr<%IoZQr^)YoU7Z_r=;i+xTAIaIq$?S>| zd-zs#w-fbg=8AY53ge03qE=$M`YqMUtBMY_^-TK6>~;t{GQ7Ka7cU0{EHkG-8u=tFtDi_^5E7A<3Ytz z;K$4?6_Hy4!iaZ~a&KAtQL%bgybJNTj;N$ox0_SkPiz1XURbcVWh;J^tD z5~ni(o`YdZbl#<%35_`*(tmnBK^~mJNkI1oBLxL;c<}pQ!V_aq(+uDxP1q<lUw;qRr52-&>kUHJZEzQJGv;*KF4~d-(Wj0BIz>v0CbLL4_9NpQcuz zGIjn+wgS=TBt1nKGntkGgeZqbE!{UXCNHlX9B>!fv;XN=zjmPu@w`)DNO zp54%@6Ofc1S;fJ_W7inc^IP=>>}qje0m&QL`)yhx9PoYsmbe)fO^XiavaUfoD1X@H z24YU)FX|JgBi;YQ7A8E`eLlSsm7Mm6E!@nz1hOEsRV+g=Y6b~u# zL8tv4E0}pFml@$YyEqjkm1`n0iCf)3cLJDo!PA@5k9jEZgOtE)2Fkggo%WaVaz^>( zv>2cW$HkAz2>|jsx;nB-gD1?1688MRJlm2N_|p}hf%8ctnWR4w-qIwH9xtxr>i$xX zmRIdwRxNMl6r=)drgWuve>|H41xvqa@g2sZCXA%mncyd5$c5n`Q>r%Y1e|1c1>cFX zz5*{tIQ-PvgE|Qg`7_9syzPo6k-)+MBlw#L+BkA@S*``WosRvLO~cA{#)81Z4b7Nj zqookh>hzHMS|8l!Jm%x~elrl+kupQ*B@Gv&Jw65bH4(i!5S8Af3HQ^;=Gj>K+ck+C zb!YV2rf-xSLpP0@i&{C4!(Njdyi@9r7kyP$zxkgy-uDbS!$DRN0QV;;PIvM^9|tB< ziiHSI*`Ce|z${ND{t(%&TG#FbZGYJa;N1eg@UmvJdi5S33Uqg5U_enbenp^XDjBu? z4^wNh%YeyZ$;`-&2q~@~jAB6JZR+$Rd~w#-pf_6lu_rwX&-3(`DfLIEye`g_8lqcB z;uL^IEY4FfKSMc9?9Pg`iUl?CV`B2o`(}9fLj)o+@aoWmq1$2;tg)}4}xl;wYcQ)Ju&(% zT=d?rm*b}_h&LmV(^r7asTR4F-|yT$x;Q%ig}%`@n%_~j>czw1bT%|*Hp6Y6;>CXG z$Ar(r)=vqnXZkYPbN}9bn-H&K{(GL^GA)&p=dZZxI@Qw*g@9zuV%l%!;Rwpd{#NBck(M%gvalPZhwlpGpM(C@$08C^6f> zg2K#^sP{Lpn0+D+GAqDla3e_ii}4L2poYoC%T6>N*F(pppV%^#`J6wj9B)AC=*X*^ zlVus&V;H#Zk}*;k5Ia<;(d_QFa#k>$nEYmWy+2y1Fd*nIq2uaPTH0l>tGBc%0|d+d zgSRdXBbk zk9ambG__NHAX57o4Xl46vC$2K9}g-NAIj)+bS}7@j~0NRba`czkU$dI%#jrc(PVcS z;S0LW!(-zL<0{bF$hVL^+);F2e;bcMddZ&D7vSSs&;oxgEEfO`;-Dl49Vdi%Xm8K1 zL3L-ikN=WNL>ooC?oT?18$Kyxwj^q`<9ZEoYW@dxqrJxP6-nNdI@Q0qqIs)W|21G3 zYDVEmF>PL(W#c#ox>QU&=Q9 zL#hz%w%8eczotOD1_qL)5VZAnG+#R?sU8^09*Yb`PRmP~snp=OzyHEgtAwt;@909a z=4E$46``Q$nCW8U*H5}(3H#wU7tD+R2-iz}%2?gRg6_!4_Qr$krSeuhN72XDY0QV0 z8zseM0_MW|RopD5CPpCrZ1E@u#k)FJql9MHy^O|GMmD!05#I`5Z~*61tAyE$-}&dx zzgpu;q|nxDZtlCZ9v?0iTRRM>I0TLVBV{zH2sh->(0a>b1z8l2g5)}}C3s2^y*E=$%%knV|RoW@f@_2vNFlghw4}tNwDqV!9w?D{4WBJ zv0&v78Z9hb3l>*@(!Kf*3RwDv)Z)t90QQ2+RbHesT=Bg(kbJbtkoIN$Tb;_!wS> zI>+=QWRzz~Z(?LE>oqWKF+2x$RHI1bsvv1@~d z(Hwvs9DI0%GEs40cKu^keDhT08x3z{R(ZdUaYB)q^H@Y86`1&V$fV17xkhg{73(t` z`!uItzpl$rGMlapq;HDw|Lv~-+g<;^Lf-h(N7YHI5LzwGt@Wd8g^68Mj+l>YV-W$P*2 zr-Uq7Uv2|F$aq)aYqS-;gb+FSL;XEcqy8TjK>cmg|5MErJ=Y*8s(Q0r^VaNWQ@3{h z>c@xEQx-OOSMh6hi_+VcSJTesoK}p%tlj(!$hs1rsYE#~3$y6I)Hb!A5h;2jt3gyp zTBJ(a5W6Y-4qj24pqOrHx#8sJW&f_%MOILZNm_7GU+0J?_fz|}Om^JAP}Q-tE3E7n zKu+r!*IfoVZ+ZI@n0sCYRe0^nme$=A-Cpn~SX-(ky{|nU3amX)%{b*%8MaEG$kG}^ z+<@9(t;}#~j1IiK+*}^Z=RLHoF`CqaX8eC1{NL4x{evz7DXQc#_@vug#JxCc%BiPv zU5hcz8nni7TU3^cP9I&R*Y-hMrbHlyR*aCwZl&_O%zNHk4`aixS^3!jrb6{rwK z?6;tHgO}AsFl}_*UP!NQ*eoJvi+p&iFN+m?mT&ZG?z7jA`ExJJ$Gxwm;_gbD_1B*J zY*f;7vz*Mtd&R=a2ZBDMLEf4hueoa->&`ciex8<;6kJ@6cRNP01}RRsv_-6zS{by* zjD|23Xg zkziQ^x=y?oGh#l>4|{KmGu+m8>dp@#%(Us@F8t${lJ<#vCJka#n_u12m?%X~b2eA3h#N+z&NTbq)SP+wGj@STieuX(Ezo9k7>TjM9SFAbwr zGy7`y7-m|*Q242FTAEAma!JTO$)VO`420^>#4c9Vi)$tIwZ&}1UnE)Xw(TSV6?4kR5wGHxV4)CJyHBCw`iSvc$3 z9T77K;TJ@1>+oJ0nc^x%-E@WBY7vN&?@L5#tK}MCeg#?{xoVb?jgq9^=>TNmRLxwmWKp;DBf zNDs^oU5~fBuJ!r!bE^iSUTN%zvjdh$DYcja9bK)n;1;ezzVo&m#aeDu+6)ngHKuIG z_TZi13c>{DnXrg!xZzeJcGI|a{erg!Md%(!qfR!5vCO{-B7Z$C8iTd#f;&i(m&O8w)Tx)T%U1FE>VyBA9$xZ%Ilj=HYYuFf*lt|quT-SYW< zRG9M|!iKJ5YENg3mu8xGSOOlXq+;_FF`lUVWKBoPrR;7$-wxXs(HBdKk4`aPtV0kM zuQGMuo(??JB_@!}7VgDNN@8^aJIsw=tmT-k17P9lyE-C#3U3O0n{k}sKWVQVX*WPQ zo^waKk55feF=AE^Ib8oj39h;ZaSz!EqMWr}IhiIY*l0}BYn*4t>p4jL1|GfZiL-um zXyBf0{dVPF+z}Iow^bq%&fln{qo2OO5e=_SkI`eiuP2fWRQmrBW6Q9sabjvBZ-++uXvqmNI5EIOh6bIo=Y?RYi#FLfFi+rKoZ d|79MWA<#C5FIR6&u!BY+2@zT0{Fiz@{|hA$3LXFe literal 0 HcmV?d00001 diff --git a/docs/en/images/permissions-module-open-dialog.png b/docs/en/images/permissions-module-open-dialog.png new file mode 100644 index 0000000000000000000000000000000000000000..46631fe12614658e6ebc3904989877002c7b361e GIT binary patch literal 25747 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 Date: Tue, 15 Jun 2021 15:13:32 +0300 Subject: [PATCH 5/7] Added Permission Management Providers section. --- docs/en/Modules/Feature-Management.md | 2 +- docs/en/Modules/Permission-Management.md | 44 ++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/docs/en/Modules/Feature-Management.md b/docs/en/Modules/Feature-Management.md index 471838e5cd..9677e2f470 100644 --- a/docs/en/Modules/Feature-Management.md +++ b/docs/en/Modules/Feature-Management.md @@ -98,7 +98,7 @@ Configure(options => }); ```` -The order of the providers are important. Providers are executed in the reverse order. That means the `CustomSettingProvider` is executed first for this example. You can insert your provider in any order in the `Providers` list. +The order of the providers are important. Providers are executed in the reverse order. That means the `CustomFeatureProvider` is executed first for this example. You can insert your provider in any order in the `Providers` list. ## See Also diff --git a/docs/en/Modules/Permission-Management.md b/docs/en/Modules/Permission-Management.md index 05426e9b4d..ba4b34d8ed 100644 --- a/docs/en/Modules/Permission-Management.md +++ b/docs/en/Modules/Permission-Management.md @@ -60,6 +60,50 @@ public class MyService : ITransientDependency } ```` +## Permission Management Providers + +Permission Management Module is extensible, just like the [permission system](../Authorization.md). You can extend it by defining permission management providers. + +[Identity Module](Identity.md) defines the following permission management providers: + +* `UserPermissionManagementProvider`: Manages user-based permissions. +* `RolePermissionManagementProvider`: Manages role-based permissions. + +`IPermissionManager` uses these providers when you get/set permissions. You can define your own provider by implementing the `IPermissionManagementProvider` or inheriting from the `PermissionManagementProvider` base class. + +**Example:** + +````csharp +public class CustomPermissionManagementProvider : PermissionManagementProvider +{ + public override string Name => "Custom"; + + public CustomPermissionManagementProvider( + IPermissionGrantRepository permissionGrantRepository, + IGuidGenerator guidGenerator, + ICurrentTenant currentTenant) + : base( + permissionGrantRepository, + guidGenerator, + currentTenant) + { + } +} +```` + +`PermissionManagementProvider` base class makes the default implementation (using the `IPermissionGrantRepository`) for you. You can override base methods as you need. Every provider must have a unique name, which is `Custom` in this example (keep it short since it is saved to database for each feature value record). + +Once you create your provider class, you should register it using the `FeatureManagementOptions` [options class](../Options.md): + +````csharp +Configure(options => +{ + options.ManagementProviders.Add(); +}); +```` + +The order of the providers are important. Providers are executed in the reverse order. That means the `CustomPermissionManagementProvider` is executed first for this example. You can insert your provider in any order in the `Providers` list. + ## See Also * [Authorization](../Authorization.md) \ No newline at end of file From 60c51c1de0533d64d4fa423a08056269ce8403bd Mon Sep 17 00:00:00 2001 From: muhammedaltug Date: Tue, 15 Jun 2021 15:29:21 +0300 Subject: [PATCH 6/7] send culture params to initCodeFlow methods --- .../src/lib/strategies/auth-flow.strategy.ts | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts b/npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts index 7c08da76a2..eae95c6a4d 100644 --- a/npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts +++ b/npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts @@ -2,7 +2,13 @@ import { HttpHeaders } from '@angular/common/http'; import { Injector } from '@angular/core'; import { Params, Router } from '@angular/router'; import { Store } from '@ngxs/store'; -import { AuthConfig, OAuthErrorEvent, OAuthInfoEvent, OAuthService, OAuthStorage } from 'angular-oauth2-oidc'; +import { + AuthConfig, + OAuthErrorEvent, + OAuthInfoEvent, + OAuthService, + OAuthStorage, +} from 'angular-oauth2-oidc'; import { from, Observable, of } from 'rxjs'; import { filter, switchMap, tap } from 'rxjs/operators'; import { RestOccurError } from '../actions/rest.actions'; @@ -32,10 +38,10 @@ export abstract class AuthFlowStrategy { protected sessionState: SessionStateService; protected appConfigService: AbpApplicationConfigurationService; - abstract checkIfInternalAuth(): boolean; + abstract checkIfInternalAuth(queryParams?: Params): boolean; abstract navigateToLogin(queryParams?: Params): void; abstract logout(queryParams?: Params): Observable; - abstract login(params?: LoginParams): Observable; + abstract login(params?: LoginParams | Params): Observable; private catchError = err => this.store.dispatch(new RestOccurError(err)); @@ -99,26 +105,28 @@ export class AuthCodeFlowStrategy extends AuthFlowStrategy { } navigateToLogin(queryParams?: Params) { - const lang = this.sessionState.getLanguage(); - const culture = { culture: lang, 'ui-culture': lang }; - this.oAuthService.initCodeFlow(null, { ...(lang && culture), ...queryParams }); + this.oAuthService.initCodeFlow('', this.getCultureParams(queryParams)); } - checkIfInternalAuth() { - this.oAuthService.initCodeFlow(); + checkIfInternalAuth(queryParams?: Params) { + this.oAuthService.initCodeFlow('', this.getCultureParams(queryParams)); return false; } logout(queryParams?: Params) { - const lang = this.sessionState.getLanguage(); - const culture = { culture: lang, 'ui-culture': lang }; - return from(this.oAuthService.revokeTokenAndLogout({ ...(lang && culture), ...queryParams })); + return from(this.oAuthService.revokeTokenAndLogout(this.getCultureParams(queryParams))); } - login() { - this.oAuthService.initCodeFlow(); + login(queryParams?: Params) { + this.oAuthService.initCodeFlow('', this.getCultureParams(queryParams)); return of(null); } + + private getCultureParams(queryParams?: Params) { + const lang = this.sessionState.getLanguage(); + const culture = { culture: lang, 'ui-culture': lang }; + return { ...(lang && culture), ...queryParams }; + } } export class AuthPasswordFlowStrategy extends AuthFlowStrategy { From a85b3675d65de7d033cb508cd98ba6f5dce9c1a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 15 Jun 2021 16:28:32 +0300 Subject: [PATCH 7/7] Documented IdentityServer Module --- docs/en/Modules/IdentityServer.md | 172 +++++++++++++++++++++++++++++- 1 file changed, 171 insertions(+), 1 deletion(-) diff --git a/docs/en/Modules/IdentityServer.md b/docs/en/Modules/IdentityServer.md index 0d2ab35c21..90e9fe19eb 100644 --- a/docs/en/Modules/IdentityServer.md +++ b/docs/en/Modules/IdentityServer.md @@ -1,3 +1,173 @@ # IdentityServer Module -TODO \ No newline at end of file +IdentityServer module provides a full integration with the [IdentityServer](https://github.com/IdentityServer/IdentityServer4) (IDS) framework, which provides advanced authentication features like single sign-on and API access control. This module persists clients, resources and other IDS-related objects to database. + +## How to Install + +This module comes as pre-installed (as NuGet/NPM packages) when you [create a new solution](https://abp.io/get-started) with the ABP Framework. You can continue to use it as package and get updates easily, or you can include its source code into your solution (see `get-source` [CLI](../CLI.md) command) to develop your custom module. + +### The Source Code + +The source code of this module can be accessed [here](https://github.com/abpframework/abp/tree/dev/modules/identityserver). The source code is licensed with [MIT](https://choosealicense.com/licenses/mit/), so you can freely use and customize it. + +## User Interface + +This module implements the domain logic and database integrations, but not provides any UI. Management UI is useful if you need to add clients and resources on the fly. In this case, you may build the management UI yourself or consider to purchase the [ABP Commercial](https://commercial.abp.io/) which provides the management UI for this module. + +## Relations to Other Modules + +This module is based on the [Identity Module](Identity.md) and have an [integration package](https://www.nuget.org/packages/Volo.Abp.Account.Web.IdentityServer) with the [Account Module](Account.md). + +## Options + +### AbpIdentityServerBuilderOptions + +`AbpIdentityServerBuilderOptions` can be configured in `PreConfigureServices` method of your Identity Server [module](https://docs.abp.io/en/abp/latest/Module-Development-Basics). Example: + +````csharp +public override void PreConfigureServices(ServiceConfigurationContext context) +{ + PreConfigure(builder => + { + //Set options here... + }); +} +```` + +`AbpIdentityServerBuilderOptions` properties: + +* `UpdateJwtSecurityTokenHandlerDefaultInboundClaimTypeMap` (default: true): Updates `JwtSecurityTokenHandler.DefaultInboundClaimTypeMap` to be compatible with Identity Server claims. +* `UpdateAbpClaimTypes` (default: true): Updates `AbpClaimTypes` to be compatible with identity server claims. +* `IntegrateToAspNetIdentity` (default: true): Integrate to ASP.NET Identity. +* `AddDeveloperSigningCredential` (default: true): Set false to suppress AddDeveloperSigningCredential() call on the IIdentityServerBuilder. + +`IIdentityServerBuilder` can be configured in `PreConfigureServices` method of your Identity Server [module](https://docs.abp.io/en/abp/latest/Module-Development-Basics). Example: + +````csharp +public override void PreConfigureServices(ServiceConfigurationContext context) +{ + PreConfigure(builder => + { + builder.AddSigningCredential(...); + }); +} +```` + +## Internals + +### Domain Layer + +#### Aggregates + +##### ApiResource + +API Resources are needed for allowing clients to request access tokens. + +* `ApiResource` (aggregate root): Represents an API resource in the system. + * `ApiSecret` (collection): secrets of the API resource. + * `ApiScope` (collection): scopes of the API resource. + * `ApiResourceClaim` (collection): claims of the API resource. + +##### Client + +Clients represent applications that can request tokens from your Identity Server. + +* `Client` (aggregate root): Represents an Identity Server client application. + * `ClientScope` (collection): Scopes of the client. + * `ClientSecret` (collection): Secrets of the client. + * `ClientGrantType` (collection): Grant types of the client. + * `ClientCorsOrigin` (collection): CORS origins of the client. + * `ClientRedirectUri` (collection): redirect URIs of the client. + * `ClientPostLogoutRedirectUri` (collection): Logout redirect URIs of the client. + * `ClientIdPRestriction` (collection): Provider restrictions of the client. + * `ClientClaim` (collection): Claims of the client. + * `ClientProperty` (collection): Custom properties of the client. + +##### PersistedGrant + +Persisted Grants stores AuthorizationCodes, RefreshTokens and UserConsent. + +* `PersistedGrant` (aggregate root): Represents PersistedGrant for identity server. + +##### IdentityResource + +Identity resources are data like user ID, name, or email address of a user. + +* `IdentityResource` (aggregate root): Represents and Identity Server identity resource. + * `IdentityClaim` (collection): Claims of identity resource. + +#### Repositories + +Following custom repositories are defined for this module: + +* `IApiResourceRepository` +* `IClientRepository` +* `IPersistentGrantRepository` +* `IIdentityResourceRepository` + +#### Domain Services + +This module doesn't contain any domain service but overrides the services below; + +* `AbpProfileService` (Used when `AbpIdentityServerBuilderOptions.IntegrateToAspNetIdentity` is true) +* `AbpClaimsService` +* `AbpCorsPolicyService` + +### Settings + +This module doesn't define any settings. + +### Application Layer + +#### Application Services + +* `ApiResourceAppService` (implements `IApiResourceAppService`): Implements the use cases of the API resource management UI. +* `IdentityServerClaimTypeAppService` (implement `IIdentityServerClaimTypeAppService`): Used to get list of claims. +* `ApiResourceAppService` (implements `IApiResourceAppService`): Implements the use cases of the API resource management UI. +* `IdentityResourceAppService` (implements `IIdentityResourceAppService`): Implements the use cases of the Identity resource management UI. + +### Database Providers + +#### Common + +##### Table/Collection Prefix & Schema + +All tables/collections use the `IdentityServer` prefix by default. Set static properties on the `AbpIdentityServerDbProperties` class if you need to change the table prefix or set a schema name (if supported by your database provider). + +##### Connection String + +This module uses `AbpIdentityServer` for the connection string name. If you don't define a connection string with this name, it fallbacks to the `Default` connection string. + +See the [connection strings](https://docs.abp.io/en/abp/latest/Connection-Strings) documentation for details. + +#### Entity Framework Core + +##### Tables + +* **IdentityServerApiResources** + * IdentityServerApiSecrets + * IdentityServerApiScopes + * IdentityServerApiScopeClaims + * IdentityServerApiClaims +* **IdentityServerClients** + * IdentityServerClientScopes + * IdentityServerClientSecrets + * IdentityServerClientGrantTypes + * IdentityServerClientCorsOrigins + * IdentityServerClientRedirectUris + * IdentityServerClientPostLogoutRedirectUris + * IdentityServerClientIdPRestrictions + * IdentityServerClientClaims + * IdentityServerClientProperties +* **IdentityServerPersistedGrants** +* **IdentityServerIdentityResources** + * IdentityServerIdentityClaims + +#### MongoDB + +##### Collections + +* **IdentityServerApiResources** +* **IdentityServerClients** +* **IdentityServerPersistedGrants** +* **IdentityServerIdentityResources** \ No newline at end of file