passbolt-styleguide
Version:
Passbolt styleguide contains common styling assets used by the different sites, plugin, etc.
319 lines (287 loc) • 9.92 kB
JavaScript
/**
* Passbolt ~ Open source password manager for teams
* Copyright (c) 2021 Passbolt SA (https://www.passbolt.com)
*
* Licensed under GNU Affero General Public License version 3 of the or any later version.
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) 2021 Passbolt SA (https://www.passbolt.com)
* @license https://opensource.org/licenses/AGPL-3.0 AGPL License
* @link https://www.passbolt.com Passbolt(tm)
* @since 3.2.0
*/
import React from "react";
import AppContext from "../../shared/context/AppContext/AppContext";
import PropTypes from "prop-types";
import SiteSettings from "../../shared/lib/Settings/SiteSettings";
import { ApiClientOptions } from "../../shared/lib/apiClient/apiClientOptions";
import { ApiClient } from "../../shared/lib/apiClient/apiClient";
import PassboltApiFetchError from "../../shared/lib/Error/PassboltApiFetchError";
import PassboltSubscriptionError from "../lib/Error/PassboltSubscriptionError";
import RbacsCollection from "../../shared/models/entity/rbac/rbacsCollection";
import AuthLogoutService from "../../shared/services/api/auth/AuthLogoutService";
import RbacApiService from "../../shared/services/api/rbac/rbacApiService";
const IS_AUTHENTICATED_CHECK_PERIOD = 60000;
/**
* The ApiApp context provider
*/
class ApiAppContextProvider extends React.Component {
constructor(props) {
super(props);
this.state = this.defaultState;
this.authLogoutService = new AuthLogoutService(this.getApiClientOptions());
}
/**
* ComponentDidMount
* Invoked immediately after component is inserted into the tree
* @return {void}
*/
async componentDidMount() {
await this.getLoggedInUser();
const siteSettings = await this.getSiteSettings();
await this.getRbacs(siteSettings);
this.initLocale();
this.removeSplashScreen();
const skeleton = document.querySelector(".temporary.skeleton");
if (skeleton) {
skeleton.remove();
}
}
/**
* Whenever the component is unmount.
*/
componentWillUnmount() {
clearTimeout(this.scheduledCheckIsAuthenticatedTimeout);
}
/**
* Default state
* @returns {object}
*/
get defaultState() {
return {
name: "api", // The application name
loggedInUser: null, // The logged in user
rbacs: null, // The logged in user
siteSettings: null, // The site settings
trustedDomain: this.baseUrl, // The site domain (use trusted domain for compatibility with browser extension applications)
basename: new URL(this.baseUrl).pathname, // Base path to be used for routing if needed ex. /workspace
getApiClientOptions: this.getApiClientOptions.bind(this), // Get the api client options
locale: null, // The locale
displayTestUserDirectoryDialogProps: {
userDirectoryTestResult: null, // The result of the test user directory
},
// @todo check if still necessary
setContext: (context) => {
this.setState(context);
},
// Navigation
onLogoutRequested: () => this.onLogoutRequested(),
onCheckIsAuthenticatedRequested: () => this.onCheckIsAuthenticatedRequested(),
// Expired session
onExpiredSession: this.onExpiredSession.bind(this),
// Subscription
onGetSubscriptionKeyRequested: () => this.onGetSubscriptionKeyRequested(),
// Locale
onRefreshLocaleRequested: this.onRefreshLocaleRequested.bind(this),
};
}
/**
* Returns true when the component can be rendered
*/
get isReady() {
return (
this.state.loggedInUser !== null &&
this.state.rbacs !== null &&
this.state.siteSettings !== null &&
this.state.locale !== null
);
}
/**
* Get the application base url
* @return {string}
*/
get baseUrl() {
const baseElement = document.getElementsByTagName("base") && document.getElementsByTagName("base")[0];
if (baseElement) {
return baseElement.attributes.href.value.replace(/\/*$/g, "");
}
console.error("Unable to retrieve the page base tag");
return "";
}
/**
* Get the API client options
* @returns {ApiClientOptions}
*/
getApiClientOptions() {
return new ApiClientOptions().setBaseUrl(this.state.trustedDomain);
}
/**
* Retrieve the logged in user.
* @returns {Promise<object>}
*/
async getLoggedInUser() {
const apiClientOptions = this.getApiClientOptions().setResourceName("users");
const apiClient = new ApiClient(apiClientOptions);
// To ensure account_recovery_user_setting is available for pages served by API
const result = await apiClient.get("me", {
"contain[account_recovery_user_setting]": "1",
});
const loggedInUser = result.body;
this.setState({ loggedInUser });
}
/**
* Retrieve the rbacs.
* @param {object} siteSettings
* @returns {Promise<void>}
*/
async getRbacs(siteSettings) {
const canIUseRbac = siteSettings.canIUse("rbacs");
if (!canIUseRbac) {
this.setState({ rbacs: new RbacsCollection() });
return;
}
const apiClientOptions = this.getApiClientOptions();
const rbacApiService = new RbacApiService(apiClientOptions);
const apiResponse = await rbacApiService.findMe({ ui_action: true, action: true });
const rbacsDto = apiResponse.body;
const rbacs = new RbacsCollection(rbacsDto, true);
this.setState({ rbacs });
}
/**
* Fetch the site settings
*/
async getSiteSettings() {
const apiClientOptions = this.getApiClientOptions().setResourceName("settings");
const apiClient = new ApiClient(apiClientOptions);
const settings = await apiClient.findAll();
const siteSettings = new SiteSettings(settings.body);
this.setState({ siteSettings });
return siteSettings;
}
/**
* Get the locale following this priority:
* 1. The user locale if set;
* 2. The organization locale;
* @warning Require the site settings to be fetch to work.
*/
async initLocale() {
const userLocale = await this.getUserLocale();
if (userLocale) {
return this.setState({ locale: userLocale.locale });
}
const organizationLocale = this.state.siteSettings.locale;
return this.setState({ locale: organizationLocale });
}
/**
* Get the user locale.
* @returns {Promise<object>}
*/
async getUserLocale() {
const userSettings = await this.getUserSettings();
const userLocaleSettings = userSettings.find((userSetting) => userSetting.property === "locale");
if (userLocaleSettings) {
return this.state.siteSettings.supportedLocales.find(
(supportedLocale) => supportedLocale.locale === userLocaleSettings.value,
);
}
}
/**
* Get the user settings.
* @returns {Promise<array>}
*/
async getUserSettings() {
const apiClientOptions = this.getApiClientOptions().setResourceName("account/settings");
const apiClient = new ApiClient(apiClientOptions);
const userSettings = await apiClient.findAll();
return userSettings.body;
}
/**
* Remove the splashscreen.
*/
removeSplashScreen() {
document.getElementsByTagName("html")[0].classList.remove("launching");
}
/**
* Listen when the user wants to logout.
*/
async onLogoutRequested() {
await this.authLogoutService.logout();
window.location.href = this.state.trustedDomain;
}
/**
* Whenever the user authentication status must be checked
* @return {Promise<boolean>}
* @throw Error if an unexpected error occurred while checking the session
*/
async onCheckIsAuthenticatedRequested() {
try {
const apiClientOptions = this.getApiClientOptions().setResourceName("auth");
const apiClient = new ApiClient(apiClientOptions);
await apiClient.get("is-authenticated");
return true;
} catch (error) {
if (error instanceof PassboltApiFetchError) {
if (error.data.code === 401) {
return false;
}
}
throw error;
}
}
/**
* Check when the user session is expired and execute the callback.
* @param {function} callback The callback to execute
*/
onExpiredSession(callback) {
this.scheduledCheckIsAuthenticatedTimeout = setTimeout(async () => {
const isAuthenticated = await this.onCheckIsAuthenticatedRequested();
if (!isAuthenticated) {
callback();
} else {
this.onExpiredSession(callback);
}
}, IS_AUTHENTICATED_CHECK_PERIOD);
}
/**
* Whenever the subsription key is requested
* @return {Promise<Object>}
* @throw Error if an unexpected error occurred while get the subscription key
*/
async onGetSubscriptionKeyRequested() {
try {
const apiClientOptions = this.getApiClientOptions().setResourceName("ee/subscription");
const apiClient = new ApiClient(apiClientOptions);
const subscription = await apiClient.get("key");
return subscription.body;
} catch (error) {
if (error instanceof PassboltApiFetchError) {
const isPaymentRequired = error.data && error.data.code === 402;
if (isPaymentRequired) {
const subscriptionKey = error.data.body;
throw new PassboltSubscriptionError(error.message, subscriptionKey);
}
}
throw error;
}
}
/**
* Refresh the organization locale
* @param locale
*/
onRefreshLocaleRequested(locale) {
this.state.siteSettings.setLocale(locale);
this.initLocale();
}
/**
* Render the component
* @returns {JSX}
*/
render() {
return <AppContext.Provider value={this.state}>{this.isReady && this.props.children}</AppContext.Provider>;
}
}
ApiAppContextProvider.propTypes = {
children: PropTypes.any, // The children components
};
export default ApiAppContextProvider;