1
0
mirror of https://github.com/bitwarden/directory-connector synced 2026-01-17 16:03:20 +00:00

Compare commits

...

13 Commits

Author SHA1 Message Date
Brandon
9970121edc clean up 2026-01-16 10:12:41 -05:00
Brandon
b273f39ab8 cleanup 2026-01-15 12:57:55 -05:00
Brandon
71fe509a00 fix import 2026-01-15 12:45:02 -05:00
Brandon
48066075fc tsconfig cleanup 2026-01-15 12:37:31 -05:00
Brandon
9f933b1d70 cleanup lock file 2026-01-15 12:30:08 -05:00
Brandon Treston
bae63858ea Merge branch 'main' into update-angular-21 2026-01-15 12:25:40 -05:00
Brandon
5a68f7af48 add missing polyfils for TextEncoder & TextDecoder 2026-01-13 16:07:30 -05:00
Brandon
98484a5762 updated jest, add babel & jest-enveironment-jsdom 2026-01-13 15:13:08 -05:00
Brandon
d104a12de3 @ngtools-webpack@21 and jest-preset-angular@16 2026-01-13 15:13:01 -05:00
Brandon
b30f001239 NG 21 WIP 2026-01-13 15:12:27 -05:00
Brandon
c9531a34af update @angular/cdk@21 2026-01-13 15:12:19 -05:00
Brandon
45bc60527f ng update 21 wip 2026-01-13 15:12:12 -05:00
Brandon
d98d9c9036 update jest to v.30.2.0 2026-01-13 15:12:02 -05:00
14 changed files with 3988 additions and 6484 deletions

View File

@@ -18,15 +18,17 @@
"prefix": "app", "prefix": "app",
"architect": { "architect": {
"build": { "build": {
"builder": "@angular-devkit/build-angular:browser", "builder": "@angular/build:application",
"options": { "options": {
"outputPath": "dist", "outputPath": {
"base": "dist"
},
"index": "src/index.html", "index": "src/index.html",
"main": "src/main.ts",
"tsConfig": "tsconfig.json", "tsConfig": "tsconfig.json",
"assets": [], "assets": [],
"styles": [], "styles": [],
"scripts": [] "scripts": [],
"browser": "src/main.ts"
} }
} }
} }

View File

@@ -13,42 +13,47 @@ import {
@Component({ @Component({
selector: "[toast-component2]", selector: "[toast-component2]",
template: ` template: `
<button @if (options.closeButton) {
*ngIf="options.closeButton" <button (click)="remove()" type="button" class="toast-close-button" aria-label="Close">
(click)="remove()" <span aria-hidden="true">&times;</span>
type="button" </button>
class="toast-close-button" }
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
</button>
<div class="icon"> <div class="icon">
<i></i> <i></i>
</div> </div>
<div> <div>
<div *ngIf="title" [class]="options.titleClass" [attr.aria-label]="title"> @if (title) {
{{ title }} <ng-container *ngIf="duplicatesCount">[{{ duplicatesCount + 1 }}]</ng-container> <div [class]="options.titleClass" [attr.aria-label]="title">
</div> {{ title }}
<div @if (duplicatesCount) {
*ngIf="message && options.enableHtml" [{{ duplicatesCount + 1 }}]
role="alertdialog" }
aria-live="polite" </div>
[class]="options.messageClass" }
[innerHTML]="message" @if (message && options.enableHtml) {
></div> <div
<div role="alertdialog"
*ngIf="message && !options.enableHtml" aria-live="polite"
role="alertdialog" [class]="options.messageClass"
aria-live="polite" [innerHTML]="message"
[class]="options.messageClass" ></div>
[attr.aria-label]="message" }
> @if (message && !options.enableHtml) {
{{ message }} <div
</div> role="alertdialog"
</div> aria-live="polite"
<div *ngIf="options.progressBar"> [class]="options.messageClass"
<div class="toast-progress" [style.width]="width + '%'"></div> [attr.aria-label]="message"
>
{{ message }}
</div>
}
</div> </div>
@if (options.progressBar) {
<div>
<div class="toast-progress" [style.width]="width + '%'"></div>
</div>
}
`, `,
animations: [ animations: [
trigger("flyInOut", [ trigger("flyInOut", [

View File

@@ -9,7 +9,7 @@ describe("SymmetricCryptoKey", () => {
new SymmetricCryptoKey(null); new SymmetricCryptoKey(null);
}; };
expect(t).toThrowError("Must provide key"); expect(t).toThrow("Must provide key");
}); });
describe("guesses encKey from key length", () => { describe("guesses encKey from key length", () => {
@@ -63,7 +63,7 @@ describe("SymmetricCryptoKey", () => {
new SymmetricCryptoKey(makeStaticByteArray(30)); new SymmetricCryptoKey(makeStaticByteArray(30));
}; };
expect(t).toThrowError("Unable to determine encType."); expect(t).toThrow("Unable to determine encType.");
}); });
}); });
}); });

9874
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -73,17 +73,17 @@
"test:types": "npx tsc --noEmit" "test:types": "npx tsc --noEmit"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "20.3.3", "@angular-eslint/eslint-plugin-template": "21.1.0",
"@angular-eslint/eslint-plugin-template": "20.7.0", "@angular-eslint/template-parser": "21.1.0",
"@angular-eslint/template-parser": "20.7.0", "@angular/build": "21.0.5",
"@angular/compiler-cli": "20.3.16", "@angular/compiler-cli": "21.0.8",
"@electron/notarize": "2.5.0", "@electron/notarize": "2.5.0",
"@electron/rebuild": "4.0.1", "@electron/rebuild": "4.0.1",
"@fluffy-spoon/substitute": "1.208.0", "@fluffy-spoon/substitute": "1.208.0",
"@microsoft/microsoft-graph-types": "2.43.1", "@microsoft/microsoft-graph-types": "2.43.1",
"@ngtools/webpack": "20.3.3", "@ngtools/webpack": "21.0.5",
"@types/inquirer": "8.2.10", "@types/inquirer": "8.2.10",
"@types/jest": "29.5.14", "@types/jest": "30.0.0",
"@types/lowdb": "1.0.15", "@types/lowdb": "1.0.15",
"@types/node": "22.19.2", "@types/node": "22.19.2",
"@types/node-fetch": "2.6.12", "@types/node-fetch": "2.6.12",
@@ -94,7 +94,9 @@
"@typescript-eslint/eslint-plugin": "8.50.0", "@typescript-eslint/eslint-plugin": "8.50.0",
"@typescript-eslint/parser": "8.50.0", "@typescript-eslint/parser": "8.50.0",
"@yao-pkg/pkg": "5.16.1", "@yao-pkg/pkg": "5.16.1",
"babel-loader": "9.2.1",
"clean-webpack-plugin": "4.0.0", "clean-webpack-plugin": "4.0.0",
"jest-environment-jsdom": "30.2.0",
"concurrently": "9.2.0", "concurrently": "9.2.0",
"copy-webpack-plugin": "13.0.0", "copy-webpack-plugin": "13.0.0",
"cross-env": "7.0.3", "cross-env": "7.0.3",
@@ -117,10 +119,10 @@
"html-loader": "5.1.0", "html-loader": "5.1.0",
"html-webpack-plugin": "5.6.3", "html-webpack-plugin": "5.6.3",
"husky": "9.1.7", "husky": "9.1.7",
"jest": "29.7.0", "jest": "30.2.0",
"jest-junit": "16.0.0", "jest-junit": "16.0.0",
"jest-mock-extended": "3.0.7", "jest-mock-extended": "4.0.0",
"jest-preset-angular": "14.6.0", "jest-preset-angular": "16.0.0",
"lint-staged": "16.2.6", "lint-staged": "16.2.6",
"mini-css-extract-plugin": "2.9.2", "mini-css-extract-plugin": "2.9.2",
"minimatch": "5.1.2", "minimatch": "5.1.2",
@@ -143,16 +145,16 @@
"zone.js": "0.15.1" "zone.js": "0.15.1"
}, },
"dependencies": { "dependencies": {
"@angular/animations": "20.3.16", "@angular/animations": "21.0.8",
"@angular/cdk": "20.2.14", "@angular/cdk": "21.0.6",
"@angular/cli": "20.3.3", "@angular/cli": "21.0.5",
"@angular/common": "20.3.16", "@angular/common": "21.0.8",
"@angular/compiler": "20.3.16", "@angular/compiler": "21.0.8",
"@angular/core": "20.3.16", "@angular/core": "21.0.8",
"@angular/forms": "20.3.16", "@angular/forms": "21.0.8",
"@angular/platform-browser": "20.3.16", "@angular/platform-browser": "21.0.8",
"@angular/platform-browser-dynamic": "20.3.16", "@angular/platform-browser-dynamic": "21.0.8",
"@angular/router": "20.3.16", "@angular/router": "21.0.8",
"@microsoft/microsoft-graph-client": "3.0.7", "@microsoft/microsoft-graph-client": "3.0.7",
"big-integer": "1.6.52", "big-integer": "1.6.52",
"bootstrap": "5.3.7", "bootstrap": "5.3.7",

View File

@@ -1,4 +1,4 @@
import { enableProdMode } from "@angular/core"; import { enableProdMode, provideZoneChangeDetection } from "@angular/core";
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic"; import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
import { isDev } from "@/jslib/electron/src/utils"; import { isDev } from "@/jslib/electron/src/utils";
@@ -11,4 +11,7 @@ if (!isDev()) {
enableProdMode(); enableProdMode();
} }
platformBrowserDynamic().bootstrapModule(AppModule, { preserveWhitespaces: true }); platformBrowserDynamic().bootstrapModule(AppModule, {
applicationProviders: [provideZoneChangeDetection()],
preserveWhitespaces: true,
});

View File

@@ -3,17 +3,25 @@
<div class="card-body"> <div class="card-body">
<p> <p>
{{ "lastGroupSync" | i18n }}: {{ "lastGroupSync" | i18n }}:
<span *ngIf="!lastGroupSync">-</span> @if (!lastGroupSync) {
<span>-</span>
}
{{ lastGroupSync | date: "medium" }} {{ lastGroupSync | date: "medium" }}
<br /> <br />
{{ "lastUserSync" | i18n }}: {{ "lastUserSync" | i18n }}:
<span *ngIf="!lastUserSync">-</span> @if (!lastUserSync) {
<span>-</span>
}
{{ lastUserSync | date: "medium" }} {{ lastUserSync | date: "medium" }}
</p> </p>
<p> <p>
{{ "syncStatus" | i18n }}: {{ "syncStatus" | i18n }}:
<strong *ngIf="syncRunning" class="text-success">{{ "running" | i18n }}</strong> @if (syncRunning) {
<strong *ngIf="!syncRunning" class="text-danger">{{ "stopped" | i18n }}</strong> <strong class="text-success">{{ "running" | i18n }}</strong>
}
@if (!syncRunning) {
<strong class="text-danger">{{ "stopped" | i18n }}</strong>
}
</p> </p>
<form #startForm [appApiAction]="startPromise" class="d-inline"> <form #startForm [appApiAction]="startPromise" class="d-inline">
<button <button
@@ -60,57 +68,85 @@
/> />
<label class="form-check-label" for="simSinceLast">{{ "testLastSync" | i18n }}</label> <label class="form-check-label" for="simSinceLast">{{ "testLastSync" | i18n }}</label>
</div> </div>
<ng-container *ngIf="!simForm.loading && (simUsers || simGroups)"> @if (!simForm.loading && (simUsers || simGroups)) {
<hr /> <hr />
<div class="row"> <div class="row">
<div class="col-lg"> <div class="col-lg">
<h4>{{ "users" | i18n }}</h4> <h4>{{ "users" | i18n }}</h4>
<ul class="bwi-ul testing-list" *ngIf="simEnabledUsers && simEnabledUsers.length"> @if (simEnabledUsers && simEnabledUsers.length) {
<li *ngFor="let u of simEnabledUsers" title="{{ u.referenceId }}"> <ul class="bwi-ul testing-list">
<i class="bwi bwi-li bwi-user"></i> @for (u of simEnabledUsers; track u) {
{{ u.displayName }} <li title="{{ u.referenceId }}">
</li> <i class="bwi bwi-li bwi-user"></i>
</ul> {{ u.displayName }}
<p *ngIf="!simEnabledUsers || !simEnabledUsers.length"> </li>
{{ "noUsers" | i18n }} }
</p> </ul>
}
@if (!simEnabledUsers || !simEnabledUsers.length) {
<p>
{{ "noUsers" | i18n }}
</p>
}
<h4>{{ "disabledUsers" | i18n }}</h4> <h4>{{ "disabledUsers" | i18n }}</h4>
<ul class="bwi-ul testing-list" *ngIf="simDisabledUsers && simDisabledUsers.length"> @if (simDisabledUsers && simDisabledUsers.length) {
<li *ngFor="let u of simDisabledUsers" title="{{ u.referenceId }}"> <ul class="bwi-ul testing-list">
<i class="bwi bwi-li bwi-user"></i> @for (u of simDisabledUsers; track u) {
{{ u.displayName }} <li title="{{ u.referenceId }}">
</li> <i class="bwi bwi-li bwi-user"></i>
</ul> {{ u.displayName }}
<p *ngIf="!simDisabledUsers || !simDisabledUsers.length"> </li>
{{ "noUsers" | i18n }} }
</p> </ul>
}
@if (!simDisabledUsers || !simDisabledUsers.length) {
<p>
{{ "noUsers" | i18n }}
</p>
}
<h4>{{ "deletedUsers" | i18n }}</h4> <h4>{{ "deletedUsers" | i18n }}</h4>
<ul class="bwi-ul testing-list" *ngIf="simDeletedUsers && simDeletedUsers.length"> @if (simDeletedUsers && simDeletedUsers.length) {
<li *ngFor="let u of simDeletedUsers" title="{{ u.referenceId }}"> <ul class="bwi-ul testing-list">
<i class="bwi bwi-li bwi-user"></i> @for (u of simDeletedUsers; track u) {
{{ u.displayName }} <li title="{{ u.referenceId }}">
</li> <i class="bwi bwi-li bwi-user"></i>
</ul> {{ u.displayName }}
<p *ngIf="!simDeletedUsers || !simDeletedUsers.length"> </li>
{{ "noUsers" | i18n }} }
</p> </ul>
}
@if (!simDeletedUsers || !simDeletedUsers.length) {
<p>
{{ "noUsers" | i18n }}
</p>
}
</div> </div>
<div class="col-lg"> <div class="col-lg">
<h4>{{ "groups" | i18n }}</h4> <h4>{{ "groups" | i18n }}</h4>
<ul class="bwi-ul testing-list" *ngIf="simGroups && simGroups.length"> @if (simGroups && simGroups.length) {
<li *ngFor="let g of simGroups" title="{{ g.referenceId }}"> <ul class="bwi-ul testing-list">
<i class="bwi bwi-li bwi-sitemap"></i> @for (g of simGroups; track g) {
{{ g.displayName }} <li title="{{ g.referenceId }}">
<ul class="small" *ngIf="g.users && g.users.length"> <i class="bwi bwi-li bwi-sitemap"></i>
<li *ngFor="let u of g.users" title="{{ u.referenceId }}"> {{ g.displayName }}
{{ u.displayName }} @if (g.users && g.users.length) {
<ul class="small">
@for (u of g.users; track u) {
<li title="{{ u.referenceId }}">
{{ u.displayName }}
</li>
}
</ul>
}
</li> </li>
</ul> }
</li> </ul>
</ul> }
<p *ngIf="!simGroups || !simGroups.length">{{ "noGroups" | i18n }}</p> @if (!simGroups || !simGroups.length) {
<p>{{ "noGroups" | i18n }}</p>
}
</div> </div>
</div> </div>
</ng-container> }
</div> </div>
</div> </div>

View File

@@ -6,9 +6,11 @@
<div class="mb-3"> <div class="mb-3">
<label for="directory" class="form-label">{{ "type" | i18n }}</label> <label for="directory" class="form-label">{{ "type" | i18n }}</label>
<select class="form-select" id="directory" name="Directory" [(ngModel)]="directory"> <select class="form-select" id="directory" name="Directory" [(ngModel)]="directory">
<option *ngFor="let o of directoryOptions" [ngValue]="o.value"> @for (o of directoryOptions; track o) {
{{ o.name }} <option [ngValue]="o.value">
</option> {{ o.name }}
</option>
}
</select> </select>
</div> </div>
<div [hidden]="directory != directoryType.Ldap"> <div [hidden]="directory != directoryType.Ldap">
@@ -51,20 +53,22 @@
<label class="form-check-label" for="ad">{{ "ldapAd" | i18n }}</label> <label class="form-check-label" for="ad">{{ "ldapAd" | i18n }}</label>
</div> </div>
</div> </div>
<div class="mb-3" *ngIf="!ldap.ad"> @if (!ldap.ad) {
<div class="form-check"> <div class="mb-3">
<input <div class="form-check">
class="form-check-input" <input
type="checkbox" class="form-check-input"
id="pagedSearch" type="checkbox"
[(ngModel)]="ldap.pagedSearch" id="pagedSearch"
name="PagedSearch" [(ngModel)]="ldap.pagedSearch"
/> name="PagedSearch"
<label class="form-check-label" for="pagedSearch">{{ />
"ldapPagedResults" | i18n <label class="form-check-label" for="pagedSearch">{{
}}</label> "ldapPagedResults" | i18n
}}</label>
</div>
</div> </div>
</div> }
<div class="mb-3"> <div class="mb-3">
<div class="form-check"> <div class="form-check">
<input <input
@@ -79,116 +83,122 @@
}}</label> }}</label>
</div> </div>
</div> </div>
<div class="ms-4" *ngIf="ldap.ssl"> @if (ldap.ssl) {
<div class="mb-3"> <div class="ms-4">
<div class="form-check"> <div class="mb-3">
<input <div class="form-check">
class="form-check-input" <input
type="radio" class="form-check-input"
[value]="false" type="radio"
id="ssl" [value]="false"
[(ngModel)]="ldap.startTls" id="ssl"
name="SSL" [(ngModel)]="ldap.startTls"
/> name="SSL"
<label class="form-check-label" for="ssl">{{ "ldapSsl" | i18n }}</label> />
<label class="form-check-label" for="ssl">{{ "ldapSsl" | i18n }}</label>
</div>
<div class="form-check">
<input
class="form-check-input"
type="radio"
[value]="true"
id="startTls"
[(ngModel)]="ldap.startTls"
name="StartTLS"
/>
<label class="form-check-label" for="startTls">{{ "ldapTls" | i18n }}</label>
</div>
</div> </div>
<div class="form-check"> @if (ldap.startTls) {
<input <div class="ms-4">
class="form-check-input" <p>{{ "ldapTlsUntrustedDesc" | i18n }}</p>
type="radio" <div class="mb-3">
[value]="true" <label for="tlsCaPath" class="form-label">{{ "ldapTlsCa" | i18n }}</label>
id="startTls" <input
[(ngModel)]="ldap.startTls" type="file"
name="StartTLS" class="form-control mb-2"
/> id="tlsCaPath_file"
<label class="form-check-label" for="startTls">{{ "ldapTls" | i18n }}</label> (change)="setSslPath('tlsCaPath')"
/>
<input
type="text"
class="form-control"
id="tlsCaPath"
name="TLSCaPath"
[(ngModel)]="ldap.tlsCaPath"
/>
</div>
</div>
}
@if (!ldap.startTls) {
<div class="ms-4">
<p>{{ "ldapSslUntrustedDesc" | i18n }}</p>
<div class="mb-3">
<label for="sslCertPath" class="form-label">{{ "ldapSslCert" | i18n }}</label>
<input
type="file"
class="form-control mb-2"
id="sslCertPath_file"
(change)="setSslPath('sslCertPath')"
/>
<input
type="text"
class="form-control"
id="sslCertPath"
name="SSLCertPath"
[(ngModel)]="ldap.sslCertPath"
/>
</div>
<div class="mb-3">
<label for="sslKeyPath" class="form-label">{{ "ldapSslKey" | i18n }}</label>
<input
type="file"
class="form-control mb-2"
id="sslKeyPath_file"
(change)="setSslPath('sslKeyPath')"
/>
<input
type="text"
class="form-control"
id="sslKeyPath"
name="SSLKeyPath"
[(ngModel)]="ldap.sslKeyPath"
/>
</div>
<div class="mb-3">
<label for="sslCaPath" class="form-label">{{ "ldapSslCa" | i18n }}</label>
<input
type="file"
class="form-control mb-2"
id="sslCaPath_file"
(change)="setSslPath('sslCaPath')"
/>
<input
type="text"
class="form-control"
id="sslCaPath"
name="SSLCaPath"
[(ngModel)]="ldap.sslCaPath"
/>
</div>
</div>
}
<div class="mb-3">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="certDoNotVerify"
[(ngModel)]="ldap.sslAllowUnauthorized"
name="CertDoNoVerify"
/>
<label class="form-check-label" for="certDoNotVerify">{{
"ldapCertDoNotVerify" | i18n
}}</label>
</div>
</div> </div>
</div> </div>
<div class="ms-4" *ngIf="ldap.startTls"> }
<p>{{ "ldapTlsUntrustedDesc" | i18n }}</p>
<div class="mb-3">
<label for="tlsCaPath" class="form-label">{{ "ldapTlsCa" | i18n }}</label>
<input
type="file"
class="form-control mb-2"
id="tlsCaPath_file"
(change)="setSslPath('tlsCaPath')"
/>
<input
type="text"
class="form-control"
id="tlsCaPath"
name="TLSCaPath"
[(ngModel)]="ldap.tlsCaPath"
/>
</div>
</div>
<div class="ms-4" *ngIf="!ldap.startTls">
<p>{{ "ldapSslUntrustedDesc" | i18n }}</p>
<div class="mb-3">
<label for="sslCertPath" class="form-label">{{ "ldapSslCert" | i18n }}</label>
<input
type="file"
class="form-control mb-2"
id="sslCertPath_file"
(change)="setSslPath('sslCertPath')"
/>
<input
type="text"
class="form-control"
id="sslCertPath"
name="SSLCertPath"
[(ngModel)]="ldap.sslCertPath"
/>
</div>
<div class="mb-3">
<label for="sslKeyPath" class="form-label">{{ "ldapSslKey" | i18n }}</label>
<input
type="file"
class="form-control mb-2"
id="sslKeyPath_file"
(change)="setSslPath('sslKeyPath')"
/>
<input
type="text"
class="form-control"
id="sslKeyPath"
name="SSLKeyPath"
[(ngModel)]="ldap.sslKeyPath"
/>
</div>
<div class="mb-3">
<label for="sslCaPath" class="form-label">{{ "ldapSslCa" | i18n }}</label>
<input
type="file"
class="form-control mb-2"
id="sslCaPath_file"
(change)="setSslPath('sslCaPath')"
/>
<input
type="text"
class="form-control"
id="sslCaPath"
name="SSLCaPath"
[(ngModel)]="ldap.sslCaPath"
/>
</div>
</div>
<div class="mb-3">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="certDoNotVerify"
[(ngModel)]="ldap.sslAllowUnauthorized"
name="CertDoNoVerify"
/>
<label class="form-check-label" for="certDoNotVerify">{{
"ldapCertDoNotVerify" | i18n
}}</label>
</div>
</div>
</div>
<div class="mb-3" [hidden]="true"> <div class="mb-3" [hidden]="true">
<div class="form-check"> <div class="form-check">
<input <input
@@ -211,10 +221,12 @@
name="Username" name="Username"
[(ngModel)]="ldap.username" [(ngModel)]="ldap.username"
/> />
<div class="form-text" *ngIf="ldap.ad">{{ "ex" | i18n }} company\admin</div> @if (ldap.ad) {
<div class="form-text" *ngIf="!ldap.ad"> <div class="form-text">{{ "ex" | i18n }} company\admin</div>
{{ "ex" | i18n }} cn=admin,dc=company,dc=com }
</div> @if (!ldap.ad) {
<div class="form-text">{{ "ex" | i18n }} cn=admin,dc=company,dc=com</div>
}
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="password" class="form-label">{{ "password" | i18n }}</label> <label for="password" class="form-label">{{ "password" | i18n }}</label>
@@ -604,18 +616,24 @@
name="UserFilter" name="UserFilter"
[(ngModel)]="sync.userFilter" [(ngModel)]="sync.userFilter"
></textarea> ></textarea>
<div class="form-text" *ngIf="directory === directoryType.Ldap"> @if (directory === directoryType.Ldap) {
{{ "ex" | i18n }} (&amp;(givenName=John)(|(l=Dallas)(l=Austin))) <div class="form-text">
</div> {{ "ex" | i18n }} (&amp;(givenName=John)(|(l=Dallas)(l=Austin)))
<div class="form-text" *ngIf="directory === directoryType.EntraID"> </div>
{{ "ex" | i18n }} exclude:joe&#64;company.com }
</div> @if (directory === directoryType.EntraID) {
<div class="form-text" *ngIf="directory === directoryType.Okta"> <div class="form-text">{{ "ex" | i18n }} exclude:joe&#64;company.com</div>
{{ "ex" | i18n }} exclude:joe&#64;company.com | profile.firstName eq "John" }
</div> @if (directory === directoryType.Okta) {
<div class="form-text" *ngIf="directory === directoryType.GSuite"> <div class="form-text">
{{ "ex" | i18n }} exclude:joe&#64;company.com | orgUnitPath=/Engineering {{ "ex" | i18n }} exclude:joe&#64;company.com | profile.firstName eq "John"
</div> </div>
}
@if (directory === directoryType.GSuite) {
<div class="form-text">
{{ "ex" | i18n }} exclude:joe&#64;company.com | orgUnitPath=/Engineering
</div>
}
</div> </div>
<div class="mb-3" [hidden]="directory != directoryType.Ldap"> <div class="mb-3" [hidden]="directory != directoryType.Ldap">
<label for="userPath" class="form-label">{{ "userPath" | i18n }}</label> <label for="userPath" class="form-label">{{ "userPath" | i18n }}</label>
@@ -681,18 +699,20 @@
name="GroupFilter" name="GroupFilter"
[(ngModel)]="sync.groupFilter" [(ngModel)]="sync.groupFilter"
></textarea> ></textarea>
<div class="form-text" *ngIf="directory === directoryType.Ldap"> @if (directory === directoryType.Ldap) {
{{ "ex" | i18n }} (&amp;(objectClass=group)(!(cn=Sales*))(!(cn=IT*))) <div class="form-text">
</div> {{ "ex" | i18n }} (&amp;(objectClass=group)(!(cn=Sales*))(!(cn=IT*)))
<div class="form-text" *ngIf="directory === directoryType.EntraID"> </div>
{{ "ex" | i18n }} include:Sales,IT }
</div> @if (directory === directoryType.EntraID) {
<div class="form-text" *ngIf="directory === directoryType.Okta"> <div class="form-text">{{ "ex" | i18n }} include:Sales,IT</div>
{{ "ex" | i18n }} include:Sales,IT | type eq "APP_GROUP" }
</div> @if (directory === directoryType.Okta) {
<div class="form-text" *ngIf="directory === directoryType.GSuite"> <div class="form-text">{{ "ex" | i18n }} include:Sales,IT | type eq "APP_GROUP"</div>
{{ "ex" | i18n }} include:Sales,IT }
</div> @if (directory === directoryType.GSuite) {
<div class="form-text">{{ "ex" | i18n }} include:Sales,IT</div>
}
</div> </div>
<div class="mb-3" [hidden]="directory != directoryType.Ldap"> <div class="mb-3" [hidden]="directory != directoryType.Ldap">
<label for="groupPath" class="form-label">{{ "groupPath" | i18n }}</label> <label for="groupPath" class="form-label">{{ "groupPath" | i18n }}</label>
@@ -703,8 +723,12 @@
name="GroupPath" name="GroupPath"
[(ngModel)]="sync.groupPath" [(ngModel)]="sync.groupPath"
/> />
<div class="form-text" *ngIf="!ldap.ad">{{ "ex" | i18n }} CN=Groups</div> @if (!ldap.ad) {
<div class="form-text" *ngIf="ldap.ad">{{ "ex" | i18n }} CN=Users</div> <div class="form-text">{{ "ex" | i18n }} CN=Groups</div>
}
@if (ldap.ad) {
<div class="form-text">{{ "ex" | i18n }} CN=Users</div>
}
</div> </div>
<div [hidden]="directory != directoryType.Ldap || ldap.ad"> <div [hidden]="directory != directoryType.Ldap || ldap.ad">
<div class="mb-3"> <div class="mb-3">

View File

@@ -28,4 +28,4 @@ $danger: map_get($theme-colors, "danger");
$secondary: map_get($theme-colors, "secondary"); $secondary: map_get($theme-colors, "secondary");
$secondary-alt: map_get($theme-colors, "secondary-alt"); $secondary-alt: map_get($theme-colors, "secondary-alt");
@import "~bootstrap/scss/bootstrap.scss"; @import "bootstrap/scss/bootstrap.scss";

View File

@@ -1,4 +1,4 @@
@import "~bootstrap/scss/_variables.scss"; @import "bootstrap/scss/_variables.scss";
html.os_windows { html.os_windows {
body { body {

View File

@@ -1,4 +1,4 @@
@import "~bootstrap/scss/_variables.scss"; @import "bootstrap/scss/_variables.scss";
body { body {
padding: 10px 0 20px 0; padding: 10px 0 20px 0;

View File

@@ -1,6 +1,6 @@
@import "~ngx-toastr/toastr"; @import "ngx-toastr/toastr";
@import "~bootstrap/scss/_variables.scss"; @import "bootstrap/scss/_variables.scss";
.toast-container { .toast-container {
.toast-close-button { .toast-close-button {

View File

@@ -1,7 +1,7 @@
import { webcrypto } from "crypto"; import { webcrypto } from "crypto";
import { TextEncoder, TextDecoder } from "util";
import "jest-preset-angular/setup-jest"; Object.assign(globalThis, { TextEncoder, TextDecoder });
Object.defineProperty(window, "CSS", { value: null }); Object.defineProperty(window, "CSS", { value: null });
Object.defineProperty(window, "getComputedStyle", { Object.defineProperty(window, "getComputedStyle", {
value: () => { value: () => {

View File

@@ -5,7 +5,7 @@
}, },
"compilerOptions": { "compilerOptions": {
"pretty": true, "pretty": true,
"moduleResolution": "node", "moduleResolution": "bundler",
"noImplicitAny": true, "noImplicitAny": true,
"target": "ES2016", "target": "ES2016",
"module": "ES2020", "module": "ES2020",