diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/client-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/client-dialog.component.ts index 4bd0aa7f28..a4d6b7bf5e 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/client-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/client-dialog.component.ts @@ -14,13 +14,13 @@ /// limitations under the License. /// -import { Component, OnDestroy, SkipSelf, ViewChild } from '@angular/core'; +import { AfterViewInit, Component, OnDestroy, SkipSelf, ViewChild } from '@angular/core'; import { DialogComponent } from '@shared/components/dialog.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { Router } from '@angular/router'; import { MatDialogRef } from '@angular/material/dialog'; -import { FormBuilder, FormGroupDirective, NgForm, UntypedFormControl } from '@angular/forms'; +import { FormGroupDirective, NgForm, UntypedFormControl } from '@angular/forms'; import { getProviderHelpLink, OAuth2Client } from '@shared/models/oauth2.models'; import { OAuth2Service } from '@core/http/oauth2.service'; import { ClientComponent } from '@home/pages/admin/oauth2/clients/client.component'; @@ -32,7 +32,7 @@ import { ErrorStateMatcher } from '@angular/material/core'; providers: [{provide: ErrorStateMatcher, useExisting: ClientDialogComponent}], styleUrls: [] }) -export class ClientDialogComponent extends DialogComponent implements OnDestroy { +export class ClientDialogComponent extends DialogComponent implements OnDestroy, AfterViewInit { submitted = false; @@ -41,7 +41,6 @@ export class ClientDialogComponent extends DialogComponent, protected router: Router, protected dialogRef: MatDialogRef, - private fb: FormBuilder, private oauth2Service: OAuth2Service, @SkipSelf() private errorStateMatcher: ErrorStateMatcher) { super(store, router, dialogRef); diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/client.component.ts b/ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/client.component.ts index 2662b97c65..a66d1592d9 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/client.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/client.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { ChangeDetectorRef, Component, Inject, Input, Optional } from '@angular/core'; +import { ChangeDetectorRef, Component, Inject, Input, OnDestroy, Optional } from '@angular/core'; import { EntityComponent } from '@home/components/entity/entity.component'; import { ClientAuthenticationMethod, @@ -54,7 +54,7 @@ import { coerceBoolean } from '@app/shared/decorators/coercion'; templateUrl: './client.component.html', styleUrls: ['./client.component.scss'] }) -export class ClientComponent extends EntityComponent { +export class ClientComponent extends EntityComponent implements OnDestroy { @Input() @coerceBoolean() @@ -109,7 +109,8 @@ export class ClientComponent extends EntityComponent, + @Optional() @Inject('entitiesTableConfig') + protected entitiesTableConfigValue: EntityTableConfig, protected cd: ChangeDetectorRef, public fb: UntypedFormBuilder) { super(store, fb, entityValue, entitiesTableConfigValue, cd); @@ -185,13 +186,13 @@ export class ClientComponent extends EntityComponent = []; + const newScopeControls: Array = []; if (entity.scope) { for (const scope of entity.scope) { - scopeControls.push(this.fb.control(scope, [Validators.required])); + newScopeControls.push(this.fb.control(scope, [Validators.required])); } } - this.entityForm.setControl('scope', this.fb.array(scopeControls)); + this.entityForm.setControl('scope', this.fb.array(newScopeControls)); } } diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/clients-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/clients-table-config.resolver.ts index 178b817175..a5993bc773 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/clients-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2/clients/clients-table-config.resolver.ts @@ -27,7 +27,6 @@ import { OAuth2ClientInfo, platformTypeTranslations } from '@shared/models/oauth2.models'; -import { UtilsService } from '@core/services/utils.service'; import { TranslateService } from '@ngx-translate/core'; import { DatePipe } from '@angular/common'; import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; @@ -40,11 +39,11 @@ import { PageLink } from '@shared/models/page/page-link'; @Injectable() export class ClientsTableConfigResolver implements Resolve> { - private readonly config: EntityTableConfig = new EntityTableConfig(); + private readonly config: EntityTableConfig = + new EntityTableConfig(); constructor(private translate: TranslateService, private datePipe: DatePipe, - private utilsService: UtilsService, private oauth2Service: OAuth2Service) { this.config.tableTitle = this.translate.instant('admin.oauth2.clients'); this.config.selectionEnabled = false; @@ -53,9 +52,7 @@ export class ClientsTableConfigResolver implements Resolve getProviderHelpLink(entity.additionalInfo.providerName) }; this.config.entityComponent = ClientComponent; this.config.headerComponent = ClientTableHeaderComponent; @@ -66,20 +63,16 @@ export class ClientsTableConfigResolver implements Resolve('createdTime', 'common.created-time', this.datePipe, '170px'), new EntityTableColumn('title', 'admin.oauth2.title', '350px'), new EntityTableColumn('platforms', 'admin.oauth2.allowed-platforms', '100%', - (clientInfo) => { - return clientInfo.platforms && clientInfo.platforms.length ? - clientInfo.platforms.map(platform => this.translate.instant(platformTypeTranslations.get(platform))).join(', ') : - this.translate.instant('admin.oauth2.all-platforms'); - }, () => ({}), false) + (clientInfo) => clientInfo.platforms && clientInfo.platforms.length ? + clientInfo.platforms.map(platform => this.translate.instant(platformTypeTranslations.get(platform))).join(', ') : + this.translate.instant('admin.oauth2.all-platforms'), () => ({}), false) ); this.config.deleteEntityTitle = (client) => this.translate.instant('admin.oauth2.delete-client-title', {clientName: client.title}); this.config.deleteEntityContent = () => this.translate.instant('admin.oauth2.delete-client-text'); this.config.entitiesFetchFunction = pageLink => this.oauth2Service.findTenantOAuth2ClientInfos(pageLink); this.config.loadEntity = id => this.oauth2Service.getOAuth2ClientById(id.id); - this.config.saveEntity = client => { - return this.oauth2Service.saveOAuth2Client(client); - } + this.config.saveEntity = client => this.oauth2Service.saveOAuth2Client(client); this.config.deleteEntity = id => this.oauth2Service.deleteOauth2Client(id.id); } diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain-table-config.resolver.ts index 7680a5d502..5e2cb5f9ad 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain-table-config.resolver.ts @@ -33,7 +33,7 @@ import { DomainComponent } from '@home/pages/admin/oauth2/domains/domain.compone import { isEqual } from '@core/utils'; import { DomainTableHeaderComponent } from '@home/pages/admin/oauth2/domains/domain-table-header.component'; import { Direction } from '@app/shared/models/page/sort-order'; -import { map } from 'rxjs'; +import { map, Observable, of, mergeMap } from 'rxjs'; @Injectable() export class DomainTableConfigResolver implements Resolve> { @@ -87,18 +87,22 @@ export class DomainTableConfigResolver implements Resolve this.domainService.getDomainInfoById(id.id); this.config.saveEntity = (domain, originalDomain) => { const clientsIds = domain.oauth2ClientInfos as Array || []; + let clientsTask: Observable; if (domain.id && !isEqual(domain.oauth2ClientInfos?.sort(), originalDomain.oauth2ClientInfos?.map(info => info.id ? info.id.id : info).sort())) { - this.domainService.updateOauth2Clients(domain.id.id, clientsIds).subscribe(); + clientsTask = this.domainService.updateOauth2Clients(domain.id.id, clientsIds); + } else { + clientsTask = of(null); } delete domain.oauth2ClientInfos; - return this.domainService.saveDomain(domain, domain.id ? [] : clientsIds).pipe( - map(domain => { - (domain as DomainInfo).oauth2ClientInfos = clientsIds; - return domain; + return clientsTask.pipe( + mergeMap(() => this.domainService.saveDomain(domain, domain.id ? [] : clientsIds)), + map(savedDomain => { + (savedDomain as DomainInfo).oauth2ClientInfos = clientsIds; + return savedDomain; }) ); - } + }; this.config.deleteEntity = id => this.domainService.deleteDomain(id.id); } diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain.component.html b/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain.component.html index eaa85ce87a..f95087b3f4 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain.component.html @@ -54,10 +54,9 @@ - diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain.component.ts b/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain.component.ts index d54c969bfe..dcfd243d81 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain.component.ts @@ -26,7 +26,6 @@ import { Store } from '@ngrx/store'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { WINDOW } from '@core/services/window.service'; import { isDefinedAndNotNull } from '@core/utils'; -import { MatButton } from '@angular/material/button'; import { MatDialog } from '@angular/material/dialog'; import { ClientDialogComponent } from '@home/pages/admin/oauth2/clients/client-dialog.component'; import { EntityType } from '@shared/models/entity-type.models'; @@ -38,7 +37,7 @@ import { EntityType } from '@shared/models/entity-type.models'; }) export class DomainComponent extends EntityComponent { - private loginProcessingUrl: string = ''; + private loginProcessingUrl = ''; entityType = EntityType; @@ -83,7 +82,7 @@ export class DomainComponent extends EntityComponent { return domainName !== '' ? `${domainName}${this.loginProcessingUrl}` : ''; } - createClient($event: Event, button: MatButton) { + createClient($event: Event) { if ($event) { $event.stopPropagation(); $event.preventDefault(); diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2/mobile-apps/mobile-app-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/admin/oauth2/mobile-apps/mobile-app-table-config.resolver.ts index 625a0ad9b4..072fac02c4 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2/mobile-apps/mobile-app-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2/mobile-apps/mobile-app-table-config.resolver.ts @@ -25,7 +25,6 @@ import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; import { MobileApp, MobileAppInfo } from '@shared/models/oauth2.models'; -import { UtilsService } from '@core/services/utils.service'; import { TranslateService } from '@ngx-translate/core'; import { DatePipe } from '@angular/common'; import { EntityType, entityTypeResources, entityTypeTranslations } from '@shared/models/entity-type.models'; @@ -34,7 +33,7 @@ import { Direction } from '@app/shared/models/page/sort-order'; import { MobileAppService } from '@core/http/mobile-app.service'; import { MobileAppComponent } from '@home/pages/admin/oauth2/mobile-apps/mobile-app.component'; import { MobileAppTableHeaderComponent } from '@home/pages/admin/oauth2/mobile-apps/mobile-app-table-header.component'; -import { map } from 'rxjs'; +import { map, Observable, of, mergeMap } from 'rxjs'; @Injectable() export class MobileAppTableConfigResolver implements Resolve> { @@ -43,7 +42,6 @@ export class MobileAppTableConfigResolver implements Resolve this.mobileAppService.getMobileAppInfoById(id.id); this.config.saveEntity = (mobileApp, originalMobileApp) => { const clientsIds = mobileApp.oauth2ClientInfos as Array || []; + let clientsTask: Observable; if (mobileApp.id && !isEqual(mobileApp.oauth2ClientInfos?.sort(), originalMobileApp.oauth2ClientInfos?.map(info => info.id ? info.id.id : info).sort())) { - this.mobileAppService.updateOauth2Clients(mobileApp.id.id, clientsIds).subscribe(); + clientsTask = this.mobileAppService.updateOauth2Clients(mobileApp.id.id, clientsIds); + } else { + clientsTask = of(null); } delete mobileApp.oauth2ClientInfos; - return this.mobileAppService.saveMobileApp(mobileApp as MobileApp, mobileApp.id ? [] : clientsIds).pipe( - map(mobileApp => { - (mobileApp as MobileAppInfo).oauth2ClientInfos = clientsIds; - return mobileApp; + return clientsTask.pipe( + mergeMap(() => this.mobileAppService.saveMobileApp(mobileApp as MobileApp, mobileApp.id ? [] : clientsIds)), + map(savedMobileApp => { + (savedMobileApp as MobileAppInfo).oauth2ClientInfos = clientsIds; + return savedMobileApp; }) ); - } + }; this.config.deleteEntity = id => this.mobileAppService.deleteMobileApp(id.id); } diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2/mobile-apps/mobile-app.component.html b/ui-ngx/src/app/modules/home/pages/admin/oauth2/mobile-apps/mobile-app.component.html index afec8125c3..a613890c95 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2/mobile-apps/mobile-app.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2/mobile-apps/mobile-app.component.html @@ -28,6 +28,9 @@ {{ 'admin.oauth2.mobile-package-max-length' | translate }} + + {{ 'admin.oauth2.mobile-package-spaces' | translate }} +
@@ -63,10 +66,9 @@ - diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2/mobile-apps/mobile-app.component.ts b/ui-ngx/src/app/modules/home/pages/admin/oauth2/mobile-apps/mobile-app.component.ts index 9f90a96621..a01699bdda 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2/mobile-apps/mobile-app.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2/mobile-apps/mobile-app.component.ts @@ -19,14 +19,11 @@ import { ChangeDetectorRef, Component, Inject } from '@angular/core'; import { EntityComponent } from '@home/components/entity/entity.component'; import { MobileAppInfo } from '@shared/models/oauth2.models'; import { AppState } from '@core/core.state'; -import { OAuth2Service } from '@core/http/oauth2.service'; import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; import { TranslateService } from '@ngx-translate/core'; import { Store } from '@ngrx/store'; import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; -import { WINDOW } from '@core/services/window.service'; import { isDefinedAndNotNull, randomAlphanumeric } from '@core/utils'; -import { MatButton } from '@angular/material/button'; import { MatDialog } from '@angular/material/dialog'; import { ClientDialogComponent } from '@home/pages/admin/oauth2/clients/client-dialog.component'; import { EntityType } from '@shared/models/entity-type.models'; @@ -42,19 +39,18 @@ export class MobileAppComponent extends EntityComponent { constructor(protected store: Store, protected translate: TranslateService, - private oauth2Service: OAuth2Service, @Inject('entity') protected entityValue: MobileAppInfo, @Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig, protected cd: ChangeDetectorRef, public fb: UntypedFormBuilder, - @Inject(WINDOW) private window: Window, private dialog: MatDialog) { super(store, fb, entityValue, entitiesTableConfigValue, cd); } buildForm(entity: MobileAppInfo): UntypedFormGroup { return this.fb.group({ - pkgName: [entity?.pkgName ? entity.pkgName : '', [Validators.required, Validators.maxLength(255)]], + pkgName: [entity?.pkgName ? entity.pkgName : '', [Validators.required, Validators.maxLength(255), + Validators.pattern(/^\S+$/)]], appSecret: [entity?.appSecret ? entity.appSecret : btoa(randomAlphanumeric(64)), [Validators.required, this.base64Format]], oauth2Enabled: isDefinedAndNotNull(entity?.oauth2Enabled) ? entity.oauth2Enabled : true, @@ -68,10 +64,10 @@ export class MobileAppComponent extends EntityComponent { appSecret: entity.appSecret, oauth2Enabled: entity.oauth2Enabled, oauth2ClientInfos: entity.oauth2ClientInfos?.map(info => info.id ? info.id.id : info) - }) + }); } - createClient($event: Event, button: MatButton) { + createClient($event: Event) { if ($event) { $event.stopPropagation(); $event.preventDefault(); diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index f5f26beeeb..fbd3765415 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -323,6 +323,7 @@ "mobile-package-hint": "For Android: your own unique Application ID. For iOS: Product bundle identifier.", "mobile-package-unique": "Application package must be unique.", "mobile-package-max-length": "Application package should be less than 256", + "mobile-package-spaces": "Application package should not contain spaces", "mobile-app-secret": "Application secret", "mobile-app-secret-hint": "Base64 encoded string representing at least 512 bits of data.", "mobile-app-secret-required": "Application secret is required.",