2021SC@SDUSC
This time, combined with the deployed web application interface of the original seafile, and then starting from the source code, find out the corresponding components on the page. Map each component of the page to the code. First from components Lower common Start under the folder.
Sub components in the header component of the home page account.js The corresponding page elements are as follows:
Next, expand the analysis and look at the schematic diagram first:
1. Distinguish between ordinary user panel and system administrator panel
Through the props passed from the parent component, the child component uses the bool attribute in propTypes to determine whether the user is an administrator
const propTypes = { isAdminPanel: PropTypes.bool, };
2. Set the state of the component
constructor(props) { super(props); this.state = { showInfo: false, //Display information userName: '', //user name contactEmail: '', //User mailbox quotaUsage: '', //Quota used quotaTotal: '', //Total quota isStaff: false, //Administrator isOrgStaff: false, //Are you a group administrator usageRate: '', // Utilization rate }; this.isFirstMounted = true; //Is this the first mount }
3. Elements rendered to the page - Avatar
<a id="my-info" onClick={this.onClickAccount} className="account-toggle no-deco d-none d-md-block" aria-label="View profile and more"> <span>{this.renderAvatar()}</span> <span className="fas fa-caret-down vam"></span> //It is an empty space. Here we fill in a little content "??" to locate the span position </a> //Avatar element}
Obtained by function renderAvatar()
renderAvatar = () => { return (<img src={appAvatarURL} width="36" height="36" className="avatar" alt={gettext('Avatar')} />);
Compared with the page, we added "the touxiang" to facilitate positioning. Let's see the figure
Click the avatar or the position occupied by the blank span, and we get the above schematic diagram. The code is as follows
<div id="user-info-popup" className={`account-popup sf-popover ${this.state.showInfo? '':'hide'}`}> <div className="outer-caret up-outer-caret"> <div className="inner-caret"></div> </div> <div className="sf-popover-con"> <div className="item o-hidden"> {this.renderAvatar()} //Avatar, user name under a box. <div className="txt">{this.state.userName}</div> </div> <div id="space-traffic"> <div className="item"> <p>{gettext('Used:')}{' '}{this.state.quotaUsage} / {this.state.quotaTotal}</p> //Used space quota / total quota <div id="quota-bar"><span id="quota-usage" className="usage" style={{width: this.state.usageRate}}></span></div> //The usage frequency is dynamically controlled by span style </div> </div> <a href={siteRoot + 'profile/'} className="item">{gettext('Settings')}</a>//system management {this.renderMenu()} <a href={siteRoot + 'accounts/logout/'} className="item">{gettext('Log out')}</a> </div> //sign out </div>
The gettext() function here is used under the internationalization component. When the user switches the language, the parameters in gettext() are converted into the language set by the user. The corresponding language here is Chinese.
Review the diagram again:
4. After analyzing the page elements, take a look at the main functions:
After the component is first mounted
onClickAccount = () => { if (this.isFirstMounted) { seafileAPI.getAccountInfo().then(resp => { //Execute after calling the interface successfully this.setState({ //The return information obtained through the api is set to the status resp, and then the page elements can be used userName: resp.data.name, contactEmail: resp.data.email, usageRate: resp.data.space_usage, quotaUsage: Utils.bytesToSize(resp.data.usage), quotaTotal: Utils.bytesToSize(resp.data.total), isStaff: resp.data.is_staff, isInstAdmin: resp.data.is_inst_admin, isOrgStaff: resp.data.is_org_staff === 1 ? true : false, showInfo: !this.state.showInfo, }); }).catch(error => { //Error message capture let errMessage = Utils.getErrorMsg(error); toaster.danger(errMessage); }); this.isFirstMounted = false; // Since the component is successfully mounted, set the state of the first mount to false } else { this.setState({showInfo: !this.state.showInfo}); //If it is not the first time to mount, there is no need to call the api } }
Another function is the renderMenu function, which is displayed on the page
In this function, first judge whether the user is in the management panel, and then set different options by judging the user identity.
renderMenu = () => { let data; const { isStaff, isOrgStaff, isInstAdmin } = this.state; if (this.props.isAdminPanel) { //If you are a system administrator if (isStaff) { data = { url: siteRoot, text: gettext('Exit System Admin') }; } else if (isOrgStaff) { data = { url: siteRoot, text: gettext('Exit Organization Admin') }; } else if (isInstAdmin) { data = { url: siteRoot, text: gettext('Exit Institution Admin') }; } } else { if (isStaff) { data = { url: `${siteRoot}sys/info/`, text: gettext('System Admin') }; } else if (isOrgStaff) { data = { url: `${siteRoot}org/useradmin/`, text: gettext('Organization Admin') }; } else if (isInstAdmin) { data = { url: `${siteRoot}inst/useradmin/`, text: gettext('Institution Admin') }; } } return data && <a href={data.url} title={data.text} className="item">{data.text}</a>; }
Through the content analysis of the English version, "System Admin" knows that we use the identity of system administrator. And no longer manage the panel.
We enter the management panel and check again: it has become exiting the management panel.
We use the system administrator to add an ordinary user 123@qq.com , observe its identity: ordinary users have no options for system management, so they can't touch the content related to background management. The security of this part needs to be tested. I wonder whether the developer tool or hacker bar can enter the background management by modifying the isStaff attribute in the state of ordinary users.
See the figure below: