@sinequa/atomic-angular 1.0.18 → 1.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -2880,7 +2880,7 @@ function withSavedSearchesFeatures() {
|
|
|
2880
2880
|
return { ...state, savedSearches: [...savedSearches] };
|
|
2881
2881
|
});
|
|
2882
2882
|
},
|
|
2883
|
-
getSavedSearch(search) {
|
|
2883
|
+
getSavedSearch(search, tab) {
|
|
2884
2884
|
if (!search)
|
|
2885
2885
|
return undefined;
|
|
2886
2886
|
const savedSearches = store.savedSearches();
|
|
@@ -2889,7 +2889,15 @@ function withSavedSearchesFeatures() {
|
|
|
2889
2889
|
if (!queryParams)
|
|
2890
2890
|
return false;
|
|
2891
2891
|
const q = queryParams.split('&').find(qp => qp.startsWith('q='));
|
|
2892
|
-
|
|
2892
|
+
const qMatch = !!q && decodeURI(q.substring(2)) === search;
|
|
2893
|
+
if (!qMatch)
|
|
2894
|
+
return false;
|
|
2895
|
+
// If tab is provided, check it; otherwise accept any tab
|
|
2896
|
+
if (tab) {
|
|
2897
|
+
const t = queryParams.split('&').find(qp => qp.startsWith('t='));
|
|
2898
|
+
return !!t && decodeURI(t.substring(2)) === tab;
|
|
2899
|
+
}
|
|
2900
|
+
return true;
|
|
2893
2901
|
});
|
|
2894
2902
|
}
|
|
2895
2903
|
})));
|
|
@@ -4644,9 +4652,10 @@ class SavedSearchesService {
|
|
|
4644
4652
|
*
|
|
4645
4653
|
* @throws {Error} If saving an empty search is attempted.
|
|
4646
4654
|
*/
|
|
4647
|
-
saveSearch(saveName) {
|
|
4648
|
-
const { text } = getState(this.queryParamsStore);
|
|
4655
|
+
saveSearch(saveName, saveTab) {
|
|
4656
|
+
const { text, tab } = getState(this.queryParamsStore);
|
|
4649
4657
|
const display = saveName || text;
|
|
4658
|
+
const tabName = saveTab || tab;
|
|
4650
4659
|
if (!display) {
|
|
4651
4660
|
console.error("Saving empty search is not allowed");
|
|
4652
4661
|
return;
|
|
@@ -4654,6 +4663,7 @@ class SavedSearchesService {
|
|
|
4654
4663
|
const savedSearch = {
|
|
4655
4664
|
url: window.location.hash.substring(1),
|
|
4656
4665
|
date: new Date().toISOString(),
|
|
4666
|
+
tab: tabName,
|
|
4657
4667
|
display
|
|
4658
4668
|
};
|
|
4659
4669
|
const savedSearches = this.userSettingsStore.savedSearches();
|
|
@@ -10930,6 +10940,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
10930
10940
|
}]
|
|
10931
10941
|
}], propDecorators: { cancel: [{ type: i0.Output, args: ["cancel"] }], success: [{ type: i0.Output, args: ["success"] }], userName: [{ type: i0.Input, args: [{ isSignal: true, alias: "userName", required: false }] }, { type: i0.Output, args: ["userNameChange"] }] } });
|
|
10932
10942
|
|
|
10943
|
+
/**
|
|
10944
|
+
* Mirrors @sinequa/atomic's `AUTH_REDIRECT_ATTEMPT_KEY` (src/authentication/constants.ts): the
|
|
10945
|
+
* sessionStorage flag set right before a provider full-page redirect (`window.location.href`).
|
|
10946
|
+
* Reading it lets us tell "an IdP redirect is under way" from "no session", without a blind timer.
|
|
10947
|
+
*/
|
|
10948
|
+
const AUTH_REDIRECT_ATTEMPT_KEY = "sinequa-auth-redirect-attempt";
|
|
10933
10949
|
/**
|
|
10934
10950
|
* Represents the LoginComponent class, which is responsible for handling the login functionality.
|
|
10935
10951
|
* This component is used to authenticate users and manage the user's authentication status.
|
|
@@ -10971,6 +10987,13 @@ class SignInComponent {
|
|
|
10971
10987
|
authenticated = signal(isAuthenticated(), ...(ngDevMode ? [{ debugName: "authenticated" }] : []));
|
|
10972
10988
|
user = signal(getState(this.principalStore), ...(ngDevMode ? [{ debugName: "user" }] : []));
|
|
10973
10989
|
expiresSoonNotified = signal(false, ...(ngDevMode ? [{ debugName: "expiresSoonNotified" }] : []));
|
|
10990
|
+
/**
|
|
10991
|
+
* Forces the credentials form to show even while `externalAuth` is true. Set when a silent
|
|
10992
|
+
* SSO/browser auth attempt fails and no OAuth/SAML provider is configured: in that case
|
|
10993
|
+
* `useSSO` only meant "unknown — try SSO, then credentials", so we fall back to the form
|
|
10994
|
+
* instead of routing to /error.
|
|
10995
|
+
*/
|
|
10996
|
+
showCredentialsFallback = signal(false, ...(ngDevMode ? [{ debugName: "showCredentialsFallback" }] : []));
|
|
10974
10997
|
constructor(destroyRef) {
|
|
10975
10998
|
this.destroyRef = destroyRef;
|
|
10976
10999
|
// If the user is already authenticated when landing here (e.g. page refresh on
|
|
@@ -10980,21 +11003,45 @@ class SignInComponent {
|
|
|
10980
11003
|
const url = this.route.snapshot.queryParams["returnUrl"] || "/";
|
|
10981
11004
|
this.router.navigateByUrl(url);
|
|
10982
11005
|
}
|
|
10983
|
-
// When authentication is delegated to the browser/proxy (SSO) or an OAuth/SAML
|
|
10984
|
-
//
|
|
10985
|
-
//
|
|
10986
|
-
//
|
|
10987
|
-
//
|
|
11006
|
+
// When authentication is delegated to the browser/proxy (SSO) or an OAuth/SAML provider, no
|
|
11007
|
+
// credentials form is shown: this screen shows a loader and starts the handshake automatically.
|
|
11008
|
+
// Drive what happens next from the OUTCOME of `login()`, never a blind timer racing the network
|
|
11009
|
+
// call (the old 5s timer fired while a slow but legitimate provider call — up to its own 10s
|
|
11010
|
+
// network timeout — was still in flight, flashing /error just before the IdP redirect):
|
|
11011
|
+
// • rejected → real failure (network timeout, 5xx, callback loop) → /error WITH the reason;
|
|
11012
|
+
// • resolved true → authenticated → the `authenticated` event / returnUrl drives navigation;
|
|
11013
|
+
// • resolved false → either an OAuth/SAML full-page redirect is under way (the redirect flag was
|
|
11014
|
+
// set just before `window.location.href`; the page is about to unload, so do
|
|
11015
|
+
// nothing and let it leave for the IdP), or no provider is configured
|
|
11016
|
+
// (ambiguous SSO → show the credentials form), or the provider returned no
|
|
11017
|
+
// redirect at all (→ /error).
|
|
10988
11018
|
if (this.externalAuth && !this.authenticated()) {
|
|
10989
|
-
|
|
11019
|
+
login()
|
|
11020
|
+
.then(result => {
|
|
11021
|
+
if (result) {
|
|
11022
|
+
this.auditService.notifyLogin();
|
|
11023
|
+
return;
|
|
11024
|
+
}
|
|
11025
|
+
const hasAutoProvider = !!(globalConfig.autoOAuthProvider || globalConfig.autoSAMLProvider);
|
|
11026
|
+
if (!hasAutoProvider) {
|
|
11027
|
+
this.showCredentialsFallback.set(true);
|
|
11028
|
+
return;
|
|
11029
|
+
}
|
|
11030
|
+
// A full-page redirect to the IdP was initiated → the page is about to unload. Routing to
|
|
11031
|
+
// /error here is exactly what caused the transient "error" flash, so skip it.
|
|
11032
|
+
if (sessionStorage.getItem(AUTH_REDIRECT_ATTEMPT_KEY))
|
|
11033
|
+
return;
|
|
11034
|
+
// Provider configured but the server returned no redirectUrl and there is no session → error.
|
|
10990
11035
|
this.router.navigate(["/error"], {
|
|
10991
11036
|
queryParams: { returnUrl: this.route.snapshot.queryParams["returnUrl"] }
|
|
10992
11037
|
});
|
|
10993
|
-
}
|
|
10994
|
-
|
|
10995
|
-
|
|
10996
|
-
|
|
10997
|
-
|
|
11038
|
+
})
|
|
11039
|
+
.catch((err) => {
|
|
11040
|
+
warn("An error occurred while logging in", err);
|
|
11041
|
+
this.auditService.notify({ type: "Login_Denied" });
|
|
11042
|
+
// Surface the failure reason on the error page (e.g. "OAuth provider not found: identity-dev").
|
|
11043
|
+
const message = (err?.errorMessage ?? err?.message) || undefined;
|
|
11044
|
+
this.router.navigate(["error"], { queryParams: { message } });
|
|
10998
11045
|
});
|
|
10999
11046
|
}
|
|
11000
11047
|
effect(() => {
|
|
@@ -11106,7 +11153,7 @@ class SignInComponent {
|
|
|
11106
11153
|
}
|
|
11107
11154
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SignInComponent, deps: [{ token: i0.DestroyRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
11108
11155
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: SignInComponent, isStandalone: true, selector: "signIn, signin, sign-in", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, username: { classPropertyName: "username", publicName: "username", isSignal: true, isRequired: false, transformFunction: null }, password: { classPropertyName: "password", publicName: "password", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { forgotPassword: "forgotPassword", username: "usernameChange", password: "passwordChange" }, host: { properties: { "class": "cn('grid h-dvh w-full place-content-center', class())" } }, providers: [provideTranslocoScope("login")], ngImport: i0, template: `
|
|
11109
|
-
@if (!authenticated() && !externalAuth) {
|
|
11156
|
+
@if (!authenticated() && (!externalAuth || showCredentialsFallback())) {
|
|
11110
11157
|
<Card
|
|
11111
11158
|
hover="no"
|
|
11112
11159
|
cdkTrapFocus
|
|
@@ -11179,7 +11226,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
11179
11226
|
CardHeaderComponent,
|
|
11180
11227
|
CardContentComponent
|
|
11181
11228
|
], providers: [provideTranslocoScope("login")], template: `
|
|
11182
|
-
@if (!authenticated() && !externalAuth) {
|
|
11229
|
+
@if (!authenticated() && (!externalAuth || showCredentialsFallback())) {
|
|
11183
11230
|
<Card
|
|
11184
11231
|
hover="no"
|
|
11185
11232
|
cdkTrapFocus
|
|
@@ -15888,7 +15935,7 @@ class SavedSearchDialog {
|
|
|
15888
15935
|
this.dialog()?.showModal();
|
|
15889
15936
|
}
|
|
15890
15937
|
async confirm() {
|
|
15891
|
-
this.savedSearchesService.saveSearch(this.saveName());
|
|
15938
|
+
this.savedSearchesService.saveSearch(this.saveName(), this.queryParamsStore.tab?.());
|
|
15892
15939
|
notify.success(this.transloco.translate("searches.saved.saved"), { duration: 2000 });
|
|
15893
15940
|
// emit close output of the component
|
|
15894
15941
|
this.dialog()?.close("dialog-confirm");
|