UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

402 lines (394 loc) 133 kB
import * as i0 from '@angular/core'; import { Injectable, Component, NgModule } from '@angular/core'; import * as i1 from '@angular/router'; import { RouterModule } from '@angular/router'; import * as i2 from '@c8y/ngx-components'; import { NavigatorNode, gettext, ActionBarItemComponent, IconDirective, C8yTranslateDirective, C8yTranslatePipe, Status, TitleComponent, BreadcrumbComponent, BreadcrumbItemComponent, HelpComponent, FormGroupComponent, RequiredInputPlaceholderDirective, DefaultValidationDirective, DropAreaComponent, CoreModule, CommonModule, EmptyStateComponent, ListGroupComponent, ForOfDirective, ListItemComponent, ListItemIconComponent, ListItemBodyComponent, ListItemCollapseComponent, ProductExperienceDirective, DatePipe, hookNavigator, hookPatternMessages, hookTab } from '@c8y/ngx-components'; import { BsDatepickerInputDirective, BsDatepickerDirective, BsDatepickerModule } from 'ngx-bootstrap/datepicker'; import { pull, isEmpty, assign } from 'lodash-es'; import * as i2$1 from '@c8y/client'; import { saveAs } from 'file-saver'; import * as i2$2 from 'ngx-bootstrap/dropdown'; import { BsDropdownDirective, BsDropdownToggleDirective, BsDropdownMenuDirective, BsDropdownModule } from 'ngx-bootstrap/dropdown'; import { NgTemplateOutlet, NgFor, NgIf, NgClass, AsyncPipe } from '@angular/common'; import * as i3 from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { PopoverDirective, PopoverModule } from 'ngx-bootstrap/popover'; import * as i1$1 from 'ngx-bootstrap/modal'; import { ButtonCheckboxDirective, ButtonsModule } from 'ngx-bootstrap/buttons'; import * as i4 from '@ngx-translate/core'; import { tap, switchMap, shareReplay } from 'rxjs/operators'; import { BehaviorSubject, pipe, firstValueFrom } from 'rxjs'; import { TooltipDirective, TooltipModule } from 'ngx-bootstrap/tooltip'; class TrustedCertificatesNavigationFactory { constructor() { this.navs = []; } async get() { if (this.navs.length === 0) { this.navs.push(new NavigatorNode({ label: gettext('Trusted certificates'), icon: 'certificate', path: '/trusted-certificates/certificates', parent: gettext('Management'), priority: 100 })); } return this.navs; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TrustedCertificatesNavigationFactory, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TrustedCertificatesNavigationFactory }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TrustedCertificatesNavigationFactory, decorators: [{ type: Injectable }] }); /* tslint:disable:max-line-length */ const MESSAGES_TRUSTED_CERTIFICATES = { '^Cannot find certificate with fingerprint (.+?).$': { gettext: gettext('Could not find {{ fingerprint }} certificate.'), placeholders: { fingerprint: '$1' } }, '^Certificate Authority is not found or not eligible for renewal for tenant (.+?)$': { gettext: gettext('Could not find Certificate Authority or it is not eligible for renewal for tenant {{ tenant }}.'), placeholders: { tenant: '$1' } }, '^Wrong proof of possession verification code used for tenant (.+?). Certificate (.+?), usage not granted.$': { gettext: gettext('Could not confirm the ownership of {{ fingerprint }} certificate: invalid proof of possession verification code provided, sign verification code with private key.'), placeholders: { tenant: '$1', fingerprint: '$2' } }, '^Proof of possession verification code used for tenant (.+?) expired. Certificate (.+?), usage not granted.': { gettext: gettext('Could not confirm the ownership of {{ fingerprint }} certificate: proof of possession verification code has expired, generate a new code and try again.'), placeholders: { tenant: '$1', fingerprint: '$2' } } }; class TabsFactory { constructor(router) { this.router = router; } get() { const tabs = []; if (this.router.url.match(/trusted-certificates/g)) { tabs.push({ icon: 'certificate', priority: 1000, label: gettext('Certificates'), path: 'trusted-certificates/certificates', orientation: 'horizontal' }); tabs.push({ icon: 'settings', priority: 900, label: gettext('Settings'), path: 'trusted-certificates/settings', orientation: 'horizontal' }); } return tabs; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TabsFactory, deps: [{ token: i1.Router }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TabsFactory }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TabsFactory, decorators: [{ type: Injectable }], ctorParameters: () => [{ type: i1.Router }] }); class CrlCheckSettingsComponent { constructor(optionsService, tenantOptionsService, alertService) { this.optionsService = optionsService; this.tenantOptionsService = tenantOptionsService; this.alertService = alertService; this.crlCheck = { online: false, offline: false }; } async ngOnInit() { this.crlCheck = { offline: (await this.optionsService.getTenantOption('configuration', 'crl.offline.check.enabled', false)), online: (await this.optionsService.getTenantOption('configuration', 'crl.online.check.enabled', false)) }; } async updateTenantOption(tenantOption) { try { await this.tenantOptionsService.update(tenantOption); this.alertService.success(gettext('CRL check configuration saved.')); } catch (er) { this.alertService.addServerFailure(er); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CrlCheckSettingsComponent, deps: [{ token: i2.OptionsService }, { token: i2$1.TenantOptionsService }, { token: i2.AlertService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: CrlCheckSettingsComponent, isStandalone: true, selector: "c8y-crl-check-settings", ngImport: i0, template: "<c8y-action-bar-item [placement]=\"'right'\">\n <div\n class=\"dropdown\"\n dropdown\n [insideClick]=\"true\"\n >\n <button\n class=\"dropdown-toggle c8y-dropdown d-flex a-i-center\"\n title=\"{{ 'CRL check' | translate }}\"\n aria-haspopup=\"true\"\n type=\"button\"\n dropdownToggle\n >\n <i\n class=\"m-r-4\"\n [c8yIcon]=\"'check-document'\"\n ></i>\n <span\n class=\"text-truncate\"\n translate\n >\n CRL check\n </span>\n <i\n class=\"m-l-4 text-primary\"\n [c8yIcon]=\"'caret-down'\"\n ></i>\n </button>\n <ul\n class=\"dropdown-menu dropdown-menu-right hidden-xs\"\n data-cy=\"register-device--dropdown\"\n *dropdownMenu\n >\n <ng-container *ngTemplateOutlet=\"dropdown\"></ng-container>\n </ul>\n\n <ul class=\"dropdown-menu dropdown-menu-right visible-xs\">\n <ng-container *ngTemplateOutlet=\"dropdown\"></ng-container>\n </ul>\n\n <ng-template #dropdown>\n <li>\n <label\n class=\"c8y-checkbox d-flex a-i-center\"\n title=\"{{ 'Online`type of checking`' | translate }}\"\n >\n <input\n type=\"checkbox\"\n [(ngModel)]=\"crlCheck.online\"\n (ngModelChange)=\"\n updateTenantOption({\n category: 'configuration',\n key: 'crl.online.check.enabled',\n value: $event\n })\n \"\n />\n <span class=\"m-r-4\"></span>\n {{ 'Online`type of checking`' | translate }}\n </label>\n </li>\n <li>\n <label\n class=\"c8y-checkbox d-flex a-i-center\"\n title=\"{{ 'Offline`type of checking`' | translate }}\"\n >\n <input\n type=\"checkbox\"\n [(ngModel)]=\"crlCheck.offline\"\n (ngModelChange)=\"\n updateTenantOption({\n category: 'configuration',\n key: 'crl.offline.check.enabled',\n value: $event\n })\n \"\n />\n <span class=\"m-r-4\"></span>\n {{ 'Offline`type of checking`' | translate }}\n </label>\n </li>\n </ng-template>\n </div>\n</c8y-action-bar-item>\n", dependencies: [{ kind: "component", type: ActionBarItemComponent, selector: "c8y-action-bar-item", inputs: ["placement", "priority", "itemClass", "injector", "groupId", "inGroupPriority"] }, { kind: "directive", type: BsDropdownDirective, selector: "[bsDropdown], [dropdown]", inputs: ["placement", "triggers", "container", "dropup", "autoClose", "isAnimated", "insideClick", "isDisabled", "isOpen"], outputs: ["isOpenChange", "onShown", "onHidden"], exportAs: ["bs-dropdown"] }, { kind: "directive", type: BsDropdownToggleDirective, selector: "[bsDropdownToggle],[dropdownToggle]", exportAs: ["bs-dropdown-toggle"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: BsDropdownMenuDirective, selector: "[bsDropdownMenu],[dropdownMenu]", exportAs: ["bs-dropdown-menu"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CrlCheckSettingsComponent, decorators: [{ type: Component, args: [{ selector: 'c8y-crl-check-settings', imports: [ ActionBarItemComponent, BsDropdownDirective, BsDropdownToggleDirective, IconDirective, C8yTranslateDirective, BsDropdownMenuDirective, NgTemplateOutlet, FormsModule, C8yTranslatePipe ], template: "<c8y-action-bar-item [placement]=\"'right'\">\n <div\n class=\"dropdown\"\n dropdown\n [insideClick]=\"true\"\n >\n <button\n class=\"dropdown-toggle c8y-dropdown d-flex a-i-center\"\n title=\"{{ 'CRL check' | translate }}\"\n aria-haspopup=\"true\"\n type=\"button\"\n dropdownToggle\n >\n <i\n class=\"m-r-4\"\n [c8yIcon]=\"'check-document'\"\n ></i>\n <span\n class=\"text-truncate\"\n translate\n >\n CRL check\n </span>\n <i\n class=\"m-l-4 text-primary\"\n [c8yIcon]=\"'caret-down'\"\n ></i>\n </button>\n <ul\n class=\"dropdown-menu dropdown-menu-right hidden-xs\"\n data-cy=\"register-device--dropdown\"\n *dropdownMenu\n >\n <ng-container *ngTemplateOutlet=\"dropdown\"></ng-container>\n </ul>\n\n <ul class=\"dropdown-menu dropdown-menu-right visible-xs\">\n <ng-container *ngTemplateOutlet=\"dropdown\"></ng-container>\n </ul>\n\n <ng-template #dropdown>\n <li>\n <label\n class=\"c8y-checkbox d-flex a-i-center\"\n title=\"{{ 'Online`type of checking`' | translate }}\"\n >\n <input\n type=\"checkbox\"\n [(ngModel)]=\"crlCheck.online\"\n (ngModelChange)=\"\n updateTenantOption({\n category: 'configuration',\n key: 'crl.online.check.enabled',\n value: $event\n })\n \"\n />\n <span class=\"m-r-4\"></span>\n {{ 'Online`type of checking`' | translate }}\n </label>\n </li>\n <li>\n <label\n class=\"c8y-checkbox d-flex a-i-center\"\n title=\"{{ 'Offline`type of checking`' | translate }}\"\n >\n <input\n type=\"checkbox\"\n [(ngModel)]=\"crlCheck.offline\"\n (ngModelChange)=\"\n updateTenantOption({\n category: 'configuration',\n key: 'crl.offline.check.enabled',\n value: $event\n })\n \"\n />\n <span class=\"m-r-4\"></span>\n {{ 'Offline`type of checking`' | translate }}\n </label>\n </li>\n </ng-template>\n </div>\n</c8y-action-bar-item>\n" }] }], ctorParameters: () => [{ type: i2.OptionsService }, { type: i2$1.TenantOptionsService }, { type: i2.AlertService }] }); class CrlSettingsComponent { constructor(crlService, alertService, modalService) { this.crlService = crlService; this.alertService = alertService; this.modalService = modalService; this.crls = [this.getEmptyCertificateRevocation()]; this.droppedFiles = []; this.today = new Date(); this.MANUAL_ENTRY_POPOVER = gettext('In this section, you can override or add individual entries to the CRL. Providing the serial number is mandatory. In case the revocation date is not set, it will be configured to the current date.'); this.FILE_UPLOAD_POPOVER = gettext('In this section, you can upload a file with the list of certificates to be revoked. The file must be in CSV format, and it should include the serial number and revocation date. If the revocation date is empty, it will be set to the current date.'); } async downloadCrl() { let arrayBuffer; try { const res = await this.crlService.downloadCrlFile(); arrayBuffer = await res.arrayBuffer(); const fileBinary = new File([arrayBuffer], 'cumulocity.crl', { type: 'application/pkix-crl' }); saveAs(fileBinary); } catch (ex) { this.alertService.addServerFailure(ex); } } downloadCrlTemplate() { const blob = new Blob([`SERIALNO,DATE\nSERIAL_NO1,${new Date().toISOString()}`], { type: 'text/csv;charset=utf-8;' }); const fileName = 'crl-template'; saveAs(blob, `${fileName}.csv`); } addCertificateRevocation() { this.crls.push(this.getEmptyCertificateRevocation()); } removeCertificateRevocation(certificateRevocation) { pull(this.crls, certificateRevocation); if (this.crls.length === 0) { this.addCertificateRevocation(); } } async save() { try { await this.confirm(); await this.saveCrls(); await this.uploadCrlFile(); await this.clearSavedData(); this.alertService.success(gettext('CRL saved.')); } catch (er) { this.alertService.addServerFailure(er); } } get isListEmpty() { return this.crls.length === 1 && isEmpty(this.crls[0].serialNumberInHex); } get isFileDropped() { return !!(this.droppedFiles && this.droppedFiles[0]); } async confirm() { const isOverrideManualEntries = !this.isListEmpty && this.isFileDropped; const status = isOverrideManualEntries ? Status.DANGER : Status.WARNING; const body = isOverrideManualEntries ? gettext('You are about to update the CRL. The CRL file content will override manual entries. Do you want to proceed?') : gettext('You are about to update the CRL. Do you want to proceed?'); await this.modalService.confirm(gettext('Update the CRL'), body, status, { ok: gettext('Update') }); } clearSavedData() { this.droppedFiles = []; this.crls = [this.getEmptyCertificateRevocation()]; } async saveCrls() { if (!this.isListEmpty) { await this.crlService.uploadCrls(this.crls); } } async uploadCrlFile() { if (this.isFileDropped) { await this.crlService.uploadCrlFile(this.droppedFiles[0].file); } } getEmptyCertificateRevocation() { return { serialNumberInHex: '' }; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CrlSettingsComponent, deps: [{ token: i2$1.CrlService }, { token: i2.AlertService }, { token: i2.ModalService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: CrlSettingsComponent, isStandalone: true, selector: "c8y-crl-settings", ngImport: i0, template: "<c8y-title>{{ 'Trusted certificates' | translate }}</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n icon=\"c8y-management\"\n label=\"{{ 'Management' | translate }}\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n icon=\"certificate\"\n label=\"{{ 'Trusted certificates' | translate }}\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<c8y-crl-check-settings></c8y-crl-check-settings>\n\n<c8y-help\n [icon]=\"'settings'\"\n src=\"/docs/device-certificate-authentication/managing-trusted-certificate-settings/#managing-trusted-certificate-settings\"\n></c8y-help>\n\n<form\n class=\"card card--fullpage\"\n #crlManualForm=\"ngForm\"\n novalidate\n>\n <div class=\"card-header separator\">\n <div class=\"card-title\">\n {{ 'Offline configuration of Certificate Revocation List (CRL)' | translate }}\n </div>\n </div>\n <div class=\"inner-scroll\">\n <div class=\"card-block\">\n <div class=\"legend form-block\">\n {{ 'Manual' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{ MANUAL_ENTRY_POPOVER | translate }}\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n ></button>\n </div>\n\n <div\n class=\"row tight-grid\"\n data-cy=\"c8y-row-grid--new-row\"\n *ngFor=\"let certificateRevocation of crls; index as index; last as isLast\"\n >\n <div class=\"col-sm-5\">\n <c8y-form-group>\n <label\n [for]=\"'serialNumber' + index\"\n translate\n >\n Serial number\n </label>\n <input\n class=\"form-control\"\n required\n [name]=\"'serialNumber' + index\"\n [id]=\"'serialNumber' + index\"\n [(ngModel)]=\"certificateRevocation.serialNumberInHex\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: '8ab34fe5476' }\"\n c8yDefaultValidation=\"colonedHexNumber\"\n />\n </c8y-form-group>\n </div>\n <div class=\"col-sm-5\">\n <c8y-form-group class=\"datepicker\">\n <label [for]=\"'validTillPicker' + index\">\n {{ 'Valid till' | translate }}\n </label>\n <input\n class=\"form-control\"\n [attr.aria-label]=\"'Date to' | translate\"\n placeholder=\"{{ 'Date to' | translate }}\"\n [name]=\"'validTillPicker' + index\"\n [id]=\"'validTillPicker' + index\"\n [(ngModel)]=\"certificateRevocation.revocationDate\"\n [bsConfig]=\"{ customTodayClass: 'today' }\"\n bsDatepicker\n [maxDate]=\"today\"\n />\n </c8y-form-group>\n </div>\n <div class=\"col-sm-2\">\n <c8y-form-group>\n <div class=\"p-t-24\">\n <button\n class=\"btn btn-link hidden-xs hidden-sm\"\n title=\"{{ 'Remove' | translate }}\"\n type=\"button\"\n *ngIf=\"crls.length > 1\"\n (click)=\"removeCertificateRevocation(certificateRevocation)\"\n >\n <i\n class=\"text-danger\"\n c8yIcon=\"minus-circle\"\n ></i>\n </button>\n <button\n class=\"btn btn-link hidden-xs hidden-sm\"\n title=\"{{ 'Add' | translate }}\"\n type=\"button\"\n (click)=\"addCertificateRevocation()\"\n *ngIf=\"isLast\"\n >\n <i\n class=\"text-primary\"\n c8yIcon=\"plus-circle\"\n ></i>\n </button>\n\n <button\n class=\"btn btn-danger btn-block btn-sm visible-xs visible-sm\"\n title=\"{{ 'Remove' | translate }}\"\n type=\"button\"\n *ngIf=\"crls.length > 1\"\n (click)=\"removeCertificateRevocation(certificateRevocation)\"\n >\n <i c8yIcon=\"minus-circle\"></i>\n {{ 'Remove' | translate }}\n </button>\n <button\n class=\"btn btn-primary btn-block btn-sm visible-xs visible-sm\"\n title=\"{{ 'Add' | translate }}\"\n type=\"button\"\n (click)=\"addCertificateRevocation()\"\n *ngIf=\"isLast\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Add' | translate }}\n </button>\n </div>\n </c8y-form-group>\n </div>\n </div>\n <div class=\"legend form-block center\">\n {{ 'or' | translate }}\n </div>\n <div class=\"legend form-block\">\n {{ 'File upload' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{ FILE_UPLOAD_POPOVER | translate }}\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n ></button>\n </div>\n\n <c8y-drop-area\n class=\"drop-area-sm\"\n [title]=\"'Upload CRL file (.csv format)' | translate\"\n name=\"uploadCrlDropArea\"\n [(ngModel)]=\"droppedFiles\"\n [message]=\"'Upload CRL file (.csv format)' | translate\"\n [loadingMessage]=\"'Uploading\u2026' | translate\"\n [accept]=\"'.csv'\"\n [maxAllowedFiles]=\"1\"\n ></c8y-drop-area>\n <p class=\"help-block has-info text-muted m-t-4\">\n {{ 'CRL file content will override manual entries.' | translate }}\n </p>\n\n <button\n class=\"btn btn-sm btn-default m-t-16\"\n title=\"{{ 'Download template' | translate }}\"\n type=\"button\"\n (click)=\"downloadCrlTemplate()\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"template\"\n ></i>\n {{ 'Download template' | translate }}\n </button>\n </div>\n </div>\n <div class=\"card-footer separator\">\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Download CRL file' | translate }}\"\n type=\"button\"\n (click)=\"downloadCrl()\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"download\"\n ></i>\n {{ 'Download CRL file' | translate }}\n </button>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Save' | translate }}\"\n type=\"submit\"\n (click)=\"save()\"\n [disabled]=\"!(crlManualForm.form.valid || isListEmpty) || (isListEmpty && !isFileDropped)\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n</form>\n", dependencies: [{ kind: "component", type: TitleComponent, selector: "c8y-title", inputs: ["pageTitleUpdate"] }, { kind: "component", type: BreadcrumbComponent, selector: "c8y-breadcrumb" }, { kind: "component", type: BreadcrumbItemComponent, selector: "c8y-breadcrumb-item", inputs: ["icon", "translate", "label", "path", "injector"] }, { kind: "component", type: CrlCheckSettingsComponent, selector: "c8y-crl-check-settings" }, { kind: "component", type: HelpComponent, selector: "c8y-help", inputs: ["src", "isCollapsed", "priority", "icon"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: DefaultValidationDirective, selector: "[c8yDefaultValidation]", inputs: ["c8yDefaultValidation"] }, { kind: "directive", type: BsDatepickerInputDirective, selector: "input[bsDatepicker]" }, { kind: "directive", type: BsDatepickerDirective, selector: "[bsDatepicker]", inputs: ["placement", "triggers", "outsideClick", "container", "outsideEsc", "isDisabled", "minDate", "maxDate", "ignoreMinMaxErrors", "minMode", "daysDisabled", "datesDisabled", "datesEnabled", "dateCustomClasses", "dateTooltipTexts", "isOpen", "bsValue", "bsConfig"], outputs: ["onShown", "onHidden", "bsValueChange"], exportAs: ["bsDatepicker"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: DropAreaComponent, selector: "c8y-drop-area", inputs: ["formControl", "title", "message", "icon", "loadingMessage", "forceHideList", "alwaysShow", "clickToOpen", "loading", "progress", "maxAllowedFiles", "files", "maxFileSizeInMegaBytes", "accept"], outputs: ["dropped"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CrlSettingsComponent, decorators: [{ type: Component, args: [{ selector: 'c8y-crl-settings', imports: [ TitleComponent, BreadcrumbComponent, BreadcrumbItemComponent, CrlCheckSettingsComponent, HelpComponent, FormsModule, PopoverDirective, NgFor, FormGroupComponent, C8yTranslateDirective, RequiredInputPlaceholderDirective, DefaultValidationDirective, BsDatepickerInputDirective, BsDatepickerDirective, NgIf, IconDirective, DropAreaComponent, C8yTranslatePipe ], template: "<c8y-title>{{ 'Trusted certificates' | translate }}</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n icon=\"c8y-management\"\n label=\"{{ 'Management' | translate }}\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n icon=\"certificate\"\n label=\"{{ 'Trusted certificates' | translate }}\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<c8y-crl-check-settings></c8y-crl-check-settings>\n\n<c8y-help\n [icon]=\"'settings'\"\n src=\"/docs/device-certificate-authentication/managing-trusted-certificate-settings/#managing-trusted-certificate-settings\"\n></c8y-help>\n\n<form\n class=\"card card--fullpage\"\n #crlManualForm=\"ngForm\"\n novalidate\n>\n <div class=\"card-header separator\">\n <div class=\"card-title\">\n {{ 'Offline configuration of Certificate Revocation List (CRL)' | translate }}\n </div>\n </div>\n <div class=\"inner-scroll\">\n <div class=\"card-block\">\n <div class=\"legend form-block\">\n {{ 'Manual' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{ MANUAL_ENTRY_POPOVER | translate }}\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n ></button>\n </div>\n\n <div\n class=\"row tight-grid\"\n data-cy=\"c8y-row-grid--new-row\"\n *ngFor=\"let certificateRevocation of crls; index as index; last as isLast\"\n >\n <div class=\"col-sm-5\">\n <c8y-form-group>\n <label\n [for]=\"'serialNumber' + index\"\n translate\n >\n Serial number\n </label>\n <input\n class=\"form-control\"\n required\n [name]=\"'serialNumber' + index\"\n [id]=\"'serialNumber' + index\"\n [(ngModel)]=\"certificateRevocation.serialNumberInHex\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: '8ab34fe5476' }\"\n c8yDefaultValidation=\"colonedHexNumber\"\n />\n </c8y-form-group>\n </div>\n <div class=\"col-sm-5\">\n <c8y-form-group class=\"datepicker\">\n <label [for]=\"'validTillPicker' + index\">\n {{ 'Valid till' | translate }}\n </label>\n <input\n class=\"form-control\"\n [attr.aria-label]=\"'Date to' | translate\"\n placeholder=\"{{ 'Date to' | translate }}\"\n [name]=\"'validTillPicker' + index\"\n [id]=\"'validTillPicker' + index\"\n [(ngModel)]=\"certificateRevocation.revocationDate\"\n [bsConfig]=\"{ customTodayClass: 'today' }\"\n bsDatepicker\n [maxDate]=\"today\"\n />\n </c8y-form-group>\n </div>\n <div class=\"col-sm-2\">\n <c8y-form-group>\n <div class=\"p-t-24\">\n <button\n class=\"btn btn-link hidden-xs hidden-sm\"\n title=\"{{ 'Remove' | translate }}\"\n type=\"button\"\n *ngIf=\"crls.length > 1\"\n (click)=\"removeCertificateRevocation(certificateRevocation)\"\n >\n <i\n class=\"text-danger\"\n c8yIcon=\"minus-circle\"\n ></i>\n </button>\n <button\n class=\"btn btn-link hidden-xs hidden-sm\"\n title=\"{{ 'Add' | translate }}\"\n type=\"button\"\n (click)=\"addCertificateRevocation()\"\n *ngIf=\"isLast\"\n >\n <i\n class=\"text-primary\"\n c8yIcon=\"plus-circle\"\n ></i>\n </button>\n\n <button\n class=\"btn btn-danger btn-block btn-sm visible-xs visible-sm\"\n title=\"{{ 'Remove' | translate }}\"\n type=\"button\"\n *ngIf=\"crls.length > 1\"\n (click)=\"removeCertificateRevocation(certificateRevocation)\"\n >\n <i c8yIcon=\"minus-circle\"></i>\n {{ 'Remove' | translate }}\n </button>\n <button\n class=\"btn btn-primary btn-block btn-sm visible-xs visible-sm\"\n title=\"{{ 'Add' | translate }}\"\n type=\"button\"\n (click)=\"addCertificateRevocation()\"\n *ngIf=\"isLast\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Add' | translate }}\n </button>\n </div>\n </c8y-form-group>\n </div>\n </div>\n <div class=\"legend form-block center\">\n {{ 'or' | translate }}\n </div>\n <div class=\"legend form-block\">\n {{ 'File upload' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{ FILE_UPLOAD_POPOVER | translate }}\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n ></button>\n </div>\n\n <c8y-drop-area\n class=\"drop-area-sm\"\n [title]=\"'Upload CRL file (.csv format)' | translate\"\n name=\"uploadCrlDropArea\"\n [(ngModel)]=\"droppedFiles\"\n [message]=\"'Upload CRL file (.csv format)' | translate\"\n [loadingMessage]=\"'Uploading\u2026' | translate\"\n [accept]=\"'.csv'\"\n [maxAllowedFiles]=\"1\"\n ></c8y-drop-area>\n <p class=\"help-block has-info text-muted m-t-4\">\n {{ 'CRL file content will override manual entries.' | translate }}\n </p>\n\n <button\n class=\"btn btn-sm btn-default m-t-16\"\n title=\"{{ 'Download template' | translate }}\"\n type=\"button\"\n (click)=\"downloadCrlTemplate()\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"template\"\n ></i>\n {{ 'Download template' | translate }}\n </button>\n </div>\n </div>\n <div class=\"card-footer separator\">\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Download CRL file' | translate }}\"\n type=\"button\"\n (click)=\"downloadCrl()\"\n >\n <i\n class=\"m-r-4\"\n c8yIcon=\"download\"\n ></i>\n {{ 'Download CRL file' | translate }}\n </button>\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Save' | translate }}\"\n type=\"submit\"\n (click)=\"save()\"\n [disabled]=\"!(crlManualForm.form.valid || isListEmpty) || (isListEmpty && !isFileDropped)\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n</form>\n" }] }], ctorParameters: () => [{ type: i2$1.CrlService }, { type: i2.AlertService }, { type: i2.ModalService }] }); const trustedCertificatesSettingsRoutes = [ { path: 'trusted-certificates/settings', component: CrlSettingsComponent, pathMatch: 'full' } ]; class CrlSettingsModule { static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CrlSettingsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); } static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: CrlSettingsModule, imports: [CoreModule, CommonModule, i1.RouterModule, i2$2.BsDropdownModule, BsDatepickerModule, PopoverModule, CrlSettingsComponent, CrlCheckSettingsComponent] }); } static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CrlSettingsModule, imports: [CoreModule, CommonModule, RouterModule.forChild(trustedCertificatesSettingsRoutes), BsDropdownModule.forRoot(), BsDatepickerModule, PopoverModule, CrlSettingsComponent, CrlCheckSettingsComponent] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: CrlSettingsModule, decorators: [{ type: NgModule, args: [{ exports: [], imports: [ CoreModule, CommonModule, RouterModule.forChild(trustedCertificatesSettingsRoutes), BsDropdownModule.forRoot(), BsDatepickerModule, PopoverModule, CrlSettingsComponent, CrlCheckSettingsComponent ] }] }] }); const PRODUCT_EXPERIENCE_TRUSTED_CERTIFICATES = { EVENT: 'trustedCertificate', CERTIFICATES: { COMPONENTS: { TRUSTED_CERTIFICATE: 'trusted-certificate' }, ACTIONS: { ADD: 'add', UPDATE: 'update', DELETE: 'delete', RENEW: 'renew' }, RESULTS: { ADD_SUCCESS: 'certificateAdded', UPDATE_SUCCESS: 'certificateUpdated', DELETE_SUCCESS: 'certificateDeleted', RENEW_SUCCESS: 'certificateRenewed' } }, VERIFICATION_CODE: { COMPONENTS: { REGENERATE_CODE: 'regenerate-unsigned-verification-code', VERIFY_CODE: 'verify-signed-verification-code', DOWNLOAD_CODE: 'download-unsigned-verification-code' }, ACTIONS: { REGENERATE: 'regenerate', VERIFY: 'verify', DOWNLOAD: 'download' }, RESULTS: { REGENERATE_SUCCESS: 'unsignedVerificationCodeRegenerated', VERIFY_SUCCESS: 'signedCodeVerified', VERIFY_FAILED: 'signedCodeVerificationFailed' } } }; class AddTrustedCertificateComponent { constructor(modal, trustedCertificateService, alertService, gainsightService) { this.modal = modal; this.trustedCertificateService = trustedCertificateService; this.alertService = alertService; this.gainsightService = gainsightService; this.maxAllowedFiles = 1; this.AUTO_REGISTRATION_POPOVER = gettext('Devices using the MQTT protocol with credentials signed by this certificate will be able to communicate with the platform without prior registration. The option does not support devices using the LWM2M protocol.'); this.trustedCertificate = { status: 'DISABLED' }; this.fileIsUploaded = false; this.result = new Promise((resolve, reject) => { this._save = resolve; this._cancel = reject; }); this.PRODUCT_EXPERIENCE = PRODUCT_EXPERIENCE_TRUSTED_CERTIFICATES; } uploadFile(dropped) { this.fileIsUploaded = false; if (dropped) { const reader = new FileReader(); reader.onload = () => { this.fileIsUploaded = true; this.trustedCertificate.certInPemFormat = reader.result; }; reader.readAsText(dropped[0].file); } } async save() { try { await this.trustedCertificateService.create(this.trustedCertificate); this.gainsightService.triggerEvent(PRODUCT_EXPERIENCE_TRUSTED_CERTIFICATES.EVENT, { component: PRODUCT_EXPERIENCE_TRUSTED_CERTIFICATES.CERTIFICATES.COMPONENTS.TRUSTED_CERTIFICATE, result: PRODUCT_EXPERIENCE_TRUSTED_CERTIFICATES.CERTIFICATES.RESULTS.ADD_SUCCESS, action: PRODUCT_EXPERIENCE_TRUSTED_CERTIFICATES.CERTIFICATES.ACTIONS.ADD }); this.alertService.success(gettext('Certificate saved.')); this._save(); } catch (ex) { this.alertService.addServerFailure(ex); } } close() { this._cancel(); this.modal.hide(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AddTrustedCertificateComponent, deps: [{ token: i1$1.BsModalRef }, { token: i2$1.TrustedCertificateService }, { token: i2.AlertService }, { token: i2.GainsightService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: AddTrustedCertificateComponent, isStandalone: true, selector: "c8y-add-trusted-certificate", ngImport: i0, template: "<div class=\"viewport-modal\">\n <div class=\"modal-header dialog-header\">\n <i c8yIcon=\"certificate\"></i>\n <div\n class=\"modal-title\"\n id=\"addCertificateModalTitle\"\n translate\n >\n Add trusted certificate\n </div>\n </div>\n <form\n #addTrustedCertificateForm=\"ngForm\"\n (ngSubmit)=\"addTrustedCertificateForm.form.valid && fileIsUploaded && save()\"\n >\n <div class=\"modal-inner-scroll\">\n <div\n class=\"modal-body\"\n id=\"addCertificateModalDescription\"\n >\n <c8y-form-group>\n <label\n for=\"certificateName\"\n translate\n >\n Certificate name\n </label>\n <input\n class=\"form-control\"\n id=\"certificateName\"\n placeholder=\"{{ 'e.g. My certificate' | translate }}\"\n name=\"certificateName\"\n type=\"text\"\n autocomplete=\"off\"\n required\n [(ngModel)]=\"trustedCertificate.name\"\n />\n </c8y-form-group>\n <c8y-form-group>\n <label\n for=\"certificate\"\n translate\n >\n Certificate\n </label>\n <c8y-drop-area\n class=\"drop-area-sm\"\n [title]=\"'Paste the certificate in PEM format.' | translate\"\n (dropped)=\"uploadFile($event)\"\n [loadingMessage]=\"'Importing, please wait.' | translate\"\n [maxAllowedFiles]=\"maxAllowedFiles\"\n ></c8y-drop-area>\n </c8y-form-group>\n <c8y-form-group>\n <label\n class=\"c8y-checkbox\"\n title=\"{{ 'Auto registration' | translate }}\"\n >\n <input\n id=\"autoRegistration\"\n name=\"autoRegistration\"\n type=\"checkbox\"\n [(ngModel)]=\"trustedCertificate.autoRegistrationEnabled\"\n />\n <span></span>\n <span>{{ 'Auto registration' | translate }}</span>\n <button\n class=\"btn-help\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{ AUTO_REGISTRATION_POPOVER | translate }}\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n ></button>\n </label>\n </c8y-form-group>\n <button\n class=\"btn\"\n name=\"certificateStatus\"\n type=\"button\"\n [(ngModel)]=\"trustedCertificate.status\"\n btnCheckbox\n btnCheckboxTrue=\"ENABLED\"\n btnCheckboxFalse=\"DISABLED\"\n >\n <span\n title=\"{{ 'Disabled`trusted certificate status`' | translate }}\"\n [hidden]=\"trustedCertificate.status !== 'DISABLED'\"\n >\n {{ 'Disabled`trusted certificate status`' | translate }}\n </span>\n <span\n title=\"{{ 'Enabled`trusted certificate status`' | translate }}\"\n [hidden]=\"trustedCertificate.status !== 'ENABLED'\"\n >\n {{ 'Enabled`trusted certificate status`' | translate }}\n </span>\n </button>\n </div>\n </div>\n <div class=\"modal-footer\">\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n (click)=\"close()\"\n >\n {{ 'Cancel' | translate }}\n </button>\n\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Add certificate' | translate }}\"\n type=\"submit\"\n [disabled]=\"\n addTrustedCertificateForm.form.invalid ||\n addTrustedCertificateForm.form.pristine ||\n !fileIsUploaded\n \"\n >\n {{ 'Add certificate' | translate }}\n </button>\n </div>\n </form>\n</div>\n", dependencies: [{ kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "component", type: DropAreaComponent, selector: "c8y-drop-area", inputs: ["formControl", "title", "message", "icon", "loadingMessage", "forceHideList", "alwaysShow", "clickToOpen", "loading", "progress", "maxAllowedFiles", "files", "maxFileSizeInMegaBytes", "accept"], outputs: ["dropped"] }, { kind: "directive", type: PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "directive", type: ButtonCheckboxDirective, selector: "[btnCheckbox]", inputs: ["btnCheckboxTrue", "btnCheckboxFalse"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AddTrustedCertificateComponent, decorators: [{ type: Component, args: [{ selector: 'c8y-add-trusted-certificate', imports: [ IconDirective, C8yTranslateDirective, FormsModule, FormGroupComponent, RequiredInputPlaceholderDirective, DropAreaComponent, PopoverDirective, ButtonCheckboxDi