diff --git a/packages/asc-web-common/api/settings/index.js b/packages/asc-web-common/api/settings/index.js index 484522f0b4..ecedbfcaf2 100644 --- a/packages/asc-web-common/api/settings/index.js +++ b/packages/asc-web-common/api/settings/index.js @@ -101,6 +101,13 @@ export function setCookieSettings(lifeTime) { }); } +export function getCookieSettings() { + return request({ + method: "get", + url: "/settings/cookiesettings.json", + }); +} + export function setLifetimeAuditSettings(data) { return request({ method: "post", diff --git a/packages/asc-web-common/store/SettingsStore.js b/packages/asc-web-common/store/SettingsStore.js index 753116b478..ad00c8fd57 100644 --- a/packages/asc-web-common/store/SettingsStore.js +++ b/packages/asc-web-common/store/SettingsStore.js @@ -32,6 +32,7 @@ class SettingsStore { trustedDomainsType = 0; ipRestrictionEnable = false; ipRestrictions = []; + sessionLifetime = "1440"; timezone = "UTC"; timezones = []; tenantAlias = ""; @@ -509,6 +510,16 @@ class SettingsStore { this.enableAdmMess = turnOn; }; + getSessionLifetime = async () => { + const res = await api.settings.getCookieSettings(); + this.sessionLifetime = res; + }; + + setSessionLifetimeSettings = async (lifeTime) => { + const res = await api.settings.setCookieSettings(lifeTime); + this.sessionLifetime = lifeTime; + }; + setIsBurgerLoading = (isBurgerLoading) => { this.isBurgerLoading = isBurgerLoading; }; diff --git a/web/ASC.Web.Client/public/locales/en/Settings.json b/web/ASC.Web.Client/public/locales/en/Settings.json index 300870c834..6178afd321 100644 --- a/web/ASC.Web.Client/public/locales/en/Settings.json +++ b/web/ASC.Web.Client/public/locales/en/Settings.json @@ -80,6 +80,7 @@ "LanguageTimeSettingsTooltip": "<0>{{text}} is a way to change the language of the whole portal for all portal users and to configure the time zone so that all the events of the ONLYOFFICE portal will be shown with the correct date and time.", "LanguageTimeSettingsTooltipDescription": "To make the parameters you set take effect click the <1>{{save}} button at the bottom of the section.<3>{{learnMore}}", "LocalFile": "Local file", + "Lifetime": "Lifetime (min)", "LogoDark": "Logo for the About/Login page", "LogoDocsEditor": "Logo for the editors header", "LogoFavicon": "Favicon", @@ -130,6 +131,11 @@ "SendNotificationAboutRestoring": "Send notification about portal restoring to users", "ServerSideEncryptionMethod": "Server Side Encryption Method", "ServiceUrl": "Service Url", + "RecoveryFileNotSelected": "Recovery error. Recovery file not selected", + "SessionLifetime": "Session Lifetime", + "SessionLifetimeDescription": "Session Lifetime allows to set time (in minutes) before the portal users will need to enter the portal credentials again in order to access the portal.", + "SessionLifetimeHelper": "After save all the users will be logged out from portal.", + "SetDefaultTitle": "Set default title", "SettingPasswordStrength": "Setting password strength", "SettingPasswordStrengthDescription": "Password Strength Settings is a way to determine the effectiveness of a password in resisting guessing and brute-force attacks.", "SettingPasswordStrengthHelper": "Use the Minimum Password Length bar to determine how long the password should be. Check the appropriate boxes below to determine the character set that must be used in the password.", diff --git a/web/ASC.Web.Client/src/components/pages/Settings/categories/security/access-portal/index.js b/web/ASC.Web.Client/src/components/pages/Settings/categories/security/access-portal/index.js index a14ce05e28..c3894808d1 100644 --- a/web/ASC.Web.Client/src/components/pages/Settings/categories/security/access-portal/index.js +++ b/web/ASC.Web.Client/src/components/pages/Settings/categories/security/access-portal/index.js @@ -9,6 +9,7 @@ import PasswordStrengthSection from "./passwordStrength"; import TrustedMailSection from "./trustedMail"; import IpSecuritySection from "./ipSecurity"; import AdminMessageSection from "./adminMessage"; +import SessionLifetimeSection from "./sessionLifetime"; import MobileView from "./mobileView"; import CategoryWrapper from "../sub-components/category-wrapper"; import { size } from "@appserver/components/utils/device"; @@ -76,6 +77,14 @@ const AccessPortal = (props) => { tooltipUrl={`https://helpcenter.onlyoffice.com/${lng}/administration/configuration.aspx#ChangingSecuritySettings_block`} /> + +
+ + ); }; diff --git a/web/ASC.Web.Client/src/components/pages/Settings/categories/security/access-portal/mobileView.js b/web/ASC.Web.Client/src/components/pages/Settings/categories/security/access-portal/mobileView.js index c0b865410b..c1a073da52 100644 --- a/web/ASC.Web.Client/src/components/pages/Settings/categories/security/access-portal/mobileView.js +++ b/web/ASC.Web.Client/src/components/pages/Settings/categories/security/access-portal/mobileView.js @@ -49,6 +49,12 @@ const MobileView = (props) => { url="/settings/security/access-portal/admin-message" onClickLink={onClickLink} /> + ); }; diff --git a/web/ASC.Web.Client/src/components/pages/Settings/categories/security/access-portal/sessionLifetime.js b/web/ASC.Web.Client/src/components/pages/Settings/categories/security/access-portal/sessionLifetime.js new file mode 100644 index 0000000000..86a2a36fb8 --- /dev/null +++ b/web/ASC.Web.Client/src/components/pages/Settings/categories/security/access-portal/sessionLifetime.js @@ -0,0 +1,223 @@ +import React, { useState, useEffect } from "react"; +import styled from "styled-components"; +import { withRouter } from "react-router"; +import { withTranslation } from "react-i18next"; +import { inject, observer } from "mobx-react"; +import RadioButtonGroup from "@appserver/components/radio-button-group"; +import Text from "@appserver/components/text"; +import TextInput from "@appserver/components/text-input"; +import toastr from "@appserver/components/toast/toastr"; +import { LearnMoreWrapper } from "../StyledSecurity"; +import { size } from "@appserver/components/utils/device"; +import { saveToSessionStorage, getFromSessionStorage } from "../../../utils"; +import SaveCancelButtons from "@appserver/components/save-cancel-buttons"; +import isEqual from "lodash/isEqual"; + +const MainContainer = styled.div` + width: 100%; + + .lifetime { + margin-top: 16px; + margin-bottom: 8px; + } + + .save-cancel-buttons { + margin-top: 24px; + } +`; + +const SessionLifetime = (props) => { + const { + t, + history, + lifetime, + setSessionLifetimeSettings, + initSettings, + isInit, + } = props; + const [type, setType] = useState(false); + const [sessionLifetime, setSessionLifetime] = useState("0"); + const [showReminder, setShowReminder] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(false); + + const getSettings = () => { + const currentSettings = getFromSessionStorage( + "currentSessionLifetimeSettings" + ); + const defaultData = { + lifetime: lifetime.toString(), + type: lifetime > 0 ? true : false, + }; + saveToSessionStorage("defaultSessionLifetimeSettings", defaultData); + + if (currentSettings) { + setSessionLifetime(currentSettings.lifetime); + setType(currentSettings.type); + } else { + setSessionLifetime(lifetime.toString()); + setType(lifetime > 0 ? true : false); + } + + if (currentSettings) { + setType(currentSettings.type); + setSessionLifetime(currentSettings.lifetime); + } else { + setType(lifetime > 0 ? true : false); + setSessionLifetime(lifetime.toString()); + } + setIsLoading(true); + }; + + useEffect(() => { + checkWidth(); + + if (!isInit) initSettings().then(() => setIsLoading(true)); + else setIsLoading(true); + + window.addEventListener("resize", checkWidth); + return () => window.removeEventListener("resize", checkWidth); + }, []); + + useEffect(() => { + if (!isInit) return; + getSettings(); + }, [isLoading]); + + useEffect(() => { + if (!isLoading) return; + + const defaultSettings = getFromSessionStorage( + "defaultSessionLifetimeSettings" + ); + const newSettings = { + lifetime: sessionLifetime, + type: type, + }; + + saveToSessionStorage("currentSessionLifetimeSettings", newSettings); + + if (isEqual(defaultSettings, newSettings)) { + setShowReminder(false); + } else { + setShowReminder(true); + } + }, [type, sessionLifetime]); + + const checkWidth = () => { + window.innerWidth > size.smallTablet && + history.location.pathname.includes("lifetime") && + history.push("/settings/security/access-portal"); + }; + + const onSelectType = (e) => { + setType(e.target.value === "enable" ? true : false); + }; + + const onChangeInput = (e) => { + setSessionLifetime(e.target.value); + }; + + const onBlurInput = () => { + !sessionLifetime ? setError(true) : setError(false); + }; + + const onFocusInput = () => { + setError(false); + }; + + const onSaveClick = async () => { + if (error) return; + try { + setSessionLifetimeSettings(sessionLifetime); + toastr.success(t("SuccessfullySaveSettingsMessage")); + saveToSessionStorage("defaultSessionLifetimeSettings", { + lifetime: lifetime, + type: type, + }); + setShowReminder(false); + } catch (error) { + toastr.error(error); + } + }; + + const onCancelClick = () => { + const defaultSettings = getFromSessionStorage( + "defaultSessionLifetimeSettings" + ); + setType(defaultSettings.type); + setSessionLifetime(defaultSettings.lifetime); + setShowReminder(false); + }; + + return ( + + + {t("SessionLifetimeHelper")} + + + + + {type && ( + <> + + {t("Lifetime")} + + + + )} + + + + ); +}; + +export default inject(({ auth, setup }) => { + const { sessionLifetime, setSessionLifetimeSettings } = auth.settingsStore; + const { initSettings, isInit } = setup; + + return { + lifetime: sessionLifetime, + setSessionLifetimeSettings, + initSettings, + isInit, + }; +})( + withTranslation(["Settings", "Common"])(withRouter(observer(SessionLifetime))) +); diff --git a/web/ASC.Web.Client/src/components/pages/Settings/index.js b/web/ASC.Web.Client/src/components/pages/Settings/index.js index bae362c58d..8c44e39ebe 100644 --- a/web/ASC.Web.Client/src/components/pages/Settings/index.js +++ b/web/ASC.Web.Client/src/components/pages/Settings/index.js @@ -20,6 +20,9 @@ const IpSecurityPage = lazy(() => const AdminMessagePage = lazy(() => import("./categories/security/access-portal/adminMessage") ); +const SessionLifetimePage = lazy(() => + import("./categories/security/access-portal/sessionLifetime") +); const CommonSettings = lazy(() => import("./categories/common/index.js")); @@ -106,6 +109,10 @@ const ADMIN_MESSAGE_PAGE_URL = combineUrl( PROXY_BASE_URL, "/security/access-portal/admin-message" ); +const SESSION_LIFETIME_PAGE_URL = combineUrl( + PROXY_BASE_URL, + "/security/access-portal/lifetime" +); const ADMINS_URL = combineUrl(PROXY_BASE_URL, "/security/access-rights/admins"); const THIRD_PARTY_URL = combineUrl( @@ -166,6 +173,11 @@ const Settings = (props) => { path={ADMIN_MESSAGE_PAGE_URL} component={AdminMessagePage} /> + { - const res = await api.settings.setCookieSettings(lifeTime); - }; - setLifetimeAuditSettings = async (data) => { const res = await api.settings.setLifetimeAuditSettings(data); };