passbolt-styleguide
Version:
Passbolt styleguide contains common styling assets used by the different sites, plugin, etc.
431 lines (384 loc) • 13.1 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 UserSettings from "../../shared/lib/Settings/UserSettings";
import RbacsCollection from "../../shared/models/entity/rbac/rbacsCollection";
import AccountEntity from "../../shared/models/entity/account/accountEntity";
import RoleServiceWorkerService from "../../shared/services/serviceWorker/role/roleServiceWorkerService";
import RbacServiceWorkerService from "../../shared/services/serviceWorker/rbac/rbacServiceWorkerService";
import SubscriptionKeyServiceWorkerService from "../../shared/services/api/subscriptionKey/SubscriptionKeyServiceWorkerService";
/**
* The ExtApp context provider
*/
class ExtAppContextProvider extends React.Component {
/**
* Default constructor
* @param props The component props
*/
constructor(props) {
super(props);
this.state = this.getDefaultState(props);
this.roleServiceWorkerService = new RoleServiceWorkerService(props.port);
this.rbacServiceWorkerService = new RbacServiceWorkerService(props.port);
this.bindCallbacks();
this.initEventHandlers();
this.hierarchyFolderCache = {}; // A cache of the last known list of folders hierarchy by ID from the App context
this.subscriptionKeyService = new SubscriptionKeyServiceWorkerService(props.port);
}
async componentDidMount() {
await this.getSiteSettings();
await this.getExtensionVersion();
this.getUserSettings();
this.getLoggedInUser();
this.initLocale();
this.getResources();
this.getFolders();
const account = await this.getAccount();
this.getGroups(account);
this.getUsers();
const skeleton = document.getElementById("temporary-skeleton");
if (skeleton) {
skeleton.remove();
}
}
bindCallbacks() {
this.handleStorageChange = this.handleStorageChange.bind(this);
this.handleIsReadyEvent = this.handleIsReadyEvent.bind(this);
}
initEventHandlers() {
this.props.storage.onChanged.addListener(this.handleStorageChange);
}
getDefaultState(props) {
return {
name: "browser-extension", // The application name
port: props.port,
storage: props.storage,
user: null,
resources: null,
folders: null,
foldersMapById: [], // A list of folders map by ID from the App context
users: null, // The current list of all users
groups: null,
loggedInUser: null,
account: null, // The account
rbacs: null,
siteSettings: null,
userSettings: null,
extensionVersion: null, // The extension version
locale: null, // The locale
isSessionLogoutByUser: false, // Is the session logout by the user
setContext: (context) => {
this.setState(context);
},
// passphrase dialog
passphraseRequestId: "",
// folder dialogs
folder: {},
folderMoveStrategyProps: {
requestId: null,
folderId: null,
foldersIds: [],
resourcesIds: [],
},
// share dialog
shareDialogProps: {
foldersIds: null,
resourcesIds: null,
},
// user dialog
editUserDialogProps: {
id: null, // The id of the current user to edit
},
deleteUserDialogProps: {
user: null,
},
deleteUserWithConflictsDialogProps: {
user: null, // The user to delete
errors: {}, // The dry run errors
},
// tag dialog
tagToEdit: null, // The current tag to edit
tagToDelete: null, // The current tag to delete
// group dialog
deleteGroupDialogProps: {
group: null, // the group to delete
numberResourcesOwned: null,
},
deleteGroupWithConflictsDialogProps: {
group: null, // the group to delete
errors: {}, // The dry run errors
},
// Resource comment dialog
resourceCommentId: null, // Selected resource comment id
mustRefreshComments: false, // Flag telling whether the current list of comments should be refreshed
// Navigation
onLogoutRequested: () => this.onLogoutRequested(),
// Expired session
onExpiredSession: this.onExpiredSession.bind(this),
// Subscription
onGetSubscriptionKeyRequested: () => this.onGetSubscriptionKeyRequested(),
// Locale
onUpdateLocaleRequested: this.onUpdateLocaleRequested.bind(this),
// Get folder hierarchy
getHierarchyFolderCache: this.getHierarchyFolderCache.bind(this),
};
}
handleIsReadyEvent(requestId) {
if (this.isReady()) {
this.props.port.emit(requestId, "SUCCESS");
} else {
this.props.port.emit(requestId, "ERROR");
}
}
/**
* Check if the application is ready to render with minimal data.
* @returns {boolean}
*/
isReady() {
return (
this.state.loggedInUser !== null &&
this.state.rbacs !== null &&
this.state.userSettings !== null &&
this.state.siteSettings !== null &&
this.state.locale !== null
);
}
/*
* =============================================================
* State initialization
* =============================================================
*/
/**
* Get the current user info from background page and set it in the state
*/
async getLoggedInUser() {
const canIUseRbac = this.state.siteSettings.canIUse("rbacs");
const loggedInUser = await this.props.port.request("passbolt.users.find-logged-in-user");
const rbacsDto = canIUseRbac ? await this.rbacServiceWorkerService.findMe() : [];
const rbacs = new RbacsCollection(rbacsDto);
this.setState({ loggedInUser, rbacs });
}
/**
* Get the list of site settings from background page and set it in the state
* Using SiteSettings
*/
async getSiteSettings() {
const settings = await this.props.port.request("passbolt.organization-settings.get");
const siteSettings = new SiteSettings(settings);
this.setState({ siteSettings });
}
/**
* Get extension version
*/
async getExtensionVersion() {
const extensionVersion = await this.props.port.request("passbolt.addon.get-version");
this.setState({ extensionVersion });
}
/**
* Get the list of resources from local storage and set it in the state
*/
async getResources() {
const storageData = await this.props.storage.local.get(["resources"]);
if (storageData.resources) {
const resources = storageData.resources;
this.setState({ resources: resources });
}
}
/**
* Get the list of folders from local storage and set it in the state
*/
async getFolders() {
const storageData = await this.props.storage.local.get(["folders"]);
if (storageData.folders) {
const folders = storageData.folders;
const foldersMapById = folders.reduce((result, folder) => {
result[folder.id] = folder;
return result;
}, {});
this.hierarchyFolderCache = {};
this.setState({ folders, foldersMapById });
}
}
/**
* Returns the list of all groups
*/
async getGroups(account) {
const storageKey = `groups-${account.id}`;
const storageData = await this.props.storage.local.get(storageKey);
if (storageData[storageKey]) {
const groups = storageData[storageKey];
this.setState({ groups: groups });
}
}
/**
* Get the list of users from local storage and set it in the state
*/
async getUsers() {
const storageData = await this.props.storage.local.get(["users"]);
if (storageData.users && storageData.users.length) {
const users = storageData.users;
this.setState({ users: users });
}
}
/**
* Get the list of user settings from local storage and set it in the state
* Using UserSettings
*/
async getUserSettings() {
const storageData = await this.props.storage.local.get(["_passbolt_data"]);
const userSettings = new UserSettings(storageData._passbolt_data.config);
this.setState({ userSettings });
}
/**
* Init the locale
*/
async initLocale() {
const { locale } = await this.props.port.request("passbolt.locale.get");
this.setState({ locale });
}
/**
* Get the account
* @returns {Promise<void>}
*/
async getAccount() {
const accountDto = await this.props.port.request("passbolt.account.get");
const account = new AccountEntity(accountDto);
this.setState({ account });
return account;
}
/**
* Get the hierarchy of a folder by ID in cache
* @param {string} id The id of the folder
* @returns {array<object>} Array of folders
*/
getHierarchyFolderCache(id) {
// When resources are not in a folder
if (id === null) {
return [];
}
// Process the hierarchy with a cache map by folder id
if (typeof this.hierarchyFolderCache[id] === "undefined") {
this.hierarchyFolderCache[id] = this.getHierarchyFolder(id);
}
return this.hierarchyFolderCache[id];
}
/**
* Get the hierarchy of a folder by ID in cache
* @param {string} id The id of the folder
* @returns {*[]}
*/
getHierarchyFolder(id) {
const hierarchy = [];
let currentFolderId = id;
while (currentFolderId) {
const folder = this.state.foldersMapById[currentFolderId];
// Prevent issue if foldersMapById is not loaded yet
if (!folder) {
return hierarchy;
}
hierarchy.unshift(folder);
currentFolderId = folder.folder_parent_id;
}
return hierarchy;
}
/*
* =============================================================
* State changes on local storage change
* =============================================================
*/
/**
* Handle the change in the storage
* @param changes
*/
handleStorageChange(changes) {
if (changes.resources && changes.resources.newValue) {
const resources = changes.resources.newValue;
this.setState({ resources });
}
if (changes._passbolt_data && changes._passbolt_data.newValue) {
const userData = changes._passbolt_data.newValue;
const userSettings = new UserSettings(userData.config);
this.setState({ userSettings });
}
if (changes.folders && changes.folders.newValue) {
const folders = changes.folders.newValue;
const foldersMapById = folders.reduce((result, folder) => {
result[folder.id] = folder;
return result;
}, {});
this.hierarchyFolderCache = {};
this.setState({ folders, foldersMapById });
}
if (changes.users && changes.users.newValue) {
const users = changes.users.newValue;
this.setState({ users });
}
const storageKey = `groups-${this.state.account.id}`;
if (changes[storageKey] && changes[storageKey].newValue) {
const groups = changes[storageKey].newValue;
this.setState({ groups });
}
}
/**
* Listen when the user wants to logout.
*/
onLogoutRequested() {
const requestLogout = () => this.props.port.request("passbolt.auth.logout", true);
// Indicate that the session is logout by the user before requesting a logout
this.setState({ isSessionLogoutByUser: true }, requestLogout);
}
/**
* Listen when the user session is expired.
* @param {function} callback The callback to execute
*/
onExpiredSession(callback) {
const displayExpiredSession = () => {
if (!this.state.isSessionLogoutByUser) {
callback();
// Flush resources to not leave sensitive data
this.setState({ resources: [] });
}
};
this.props.port.on("passbolt.auth.after-logout", displayExpiredSession);
}
/**
* Whenever the subscription key is requested
*/
async onGetSubscriptionKeyRequested() {
return this.subscriptionKeyService.findOrganizationSubscriptionKey();
}
/**
* Whenever the update of the locale is requested
*/
async onUpdateLocaleRequested() {
const { locale } = await this.props.port.request("passbolt.locale.get");
this.setState({ locale });
}
/**
* Render the component
* @returns {JSX}
*/
render() {
return <AppContext.Provider value={this.state}>{this.isReady() && this.props.children}</AppContext.Provider>;
}
}
ExtAppContextProvider.propTypes = {
port: PropTypes.object,
storage: PropTypes.object,
children: PropTypes.any, // The children components
};
export default ExtAppContextProvider;