mirror of
https://github.com/pgsty/minio.git
synced 2026-09-21 18:28:24 +03:00
Revert "deprecate embedded browser (#12163)"
This reverts commit 736d8cbac4.
Bring contrib files for older contributions
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
import { Modal } from "react-bootstrap"
|
||||
import logo from "../../img/logo.svg"
|
||||
|
||||
export const AboutModal = ({ serverInfo, hideAbout }) => {
|
||||
const { version, platform, runtime } = serverInfo
|
||||
return (
|
||||
<Modal
|
||||
className="modal-about modal-dark"
|
||||
animation={false}
|
||||
show={true}
|
||||
onHide={hideAbout}
|
||||
>
|
||||
<button className="close" onClick={hideAbout}>
|
||||
<span>×</span>
|
||||
</button>
|
||||
<div className="ma-inner">
|
||||
<div className="mai-item hidden-xs">
|
||||
<a href="https://min.io" target="_blank">
|
||||
<img className="maii-logo" src={logo} alt="" />
|
||||
</a>
|
||||
</div>
|
||||
<div className="mai-item">
|
||||
<ul className="maii-list">
|
||||
<li>
|
||||
<div>Version</div>
|
||||
<small>{version}</small>
|
||||
</li>
|
||||
<li>
|
||||
<div>Platform</div>
|
||||
<small>{platform}</small>
|
||||
</li>
|
||||
<li>
|
||||
<div>Runtime</div>
|
||||
<small>{runtime}</small>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default AboutModal
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
import classNames from "classnames"
|
||||
import { connect } from "react-redux"
|
||||
import SideBar from "./SideBar"
|
||||
import MainContent from "./MainContent"
|
||||
import AlertContainer from "../alert/AlertContainer"
|
||||
|
||||
class Browser extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div
|
||||
className={classNames({
|
||||
"file-explorer": true
|
||||
})}
|
||||
>
|
||||
<SideBar />
|
||||
<MainContent />
|
||||
<AlertContainer />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(state => state)(Browser)
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
import { connect } from "react-redux"
|
||||
import { Dropdown } from "react-bootstrap"
|
||||
import * as browserActions from "./actions"
|
||||
import web from "../web"
|
||||
import history from "../history"
|
||||
import AboutModal from "./AboutModal"
|
||||
import ChangePasswordModal from "./ChangePasswordModal"
|
||||
|
||||
export class BrowserDropdown extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
showAboutModal: false,
|
||||
showChangePasswordModal: false
|
||||
}
|
||||
}
|
||||
showAbout(e) {
|
||||
e.preventDefault()
|
||||
this.setState({
|
||||
showAboutModal: true
|
||||
})
|
||||
}
|
||||
hideAbout() {
|
||||
this.setState({
|
||||
showAboutModal: false
|
||||
})
|
||||
}
|
||||
showChangePassword(e) {
|
||||
e.preventDefault()
|
||||
this.setState({
|
||||
showChangePasswordModal: true
|
||||
})
|
||||
}
|
||||
hideChangePassword() {
|
||||
this.setState({
|
||||
showChangePasswordModal: false
|
||||
})
|
||||
}
|
||||
componentDidMount() {
|
||||
const { fetchServerInfo } = this.props
|
||||
fetchServerInfo()
|
||||
}
|
||||
logout(e) {
|
||||
e.preventDefault()
|
||||
web.Logout()
|
||||
history.replace("/login")
|
||||
}
|
||||
render() {
|
||||
const { serverInfo } = this.props
|
||||
return (
|
||||
<li>
|
||||
<Dropdown pullRight id="top-right-menu">
|
||||
<Dropdown.Toggle noCaret>
|
||||
<i className="fas fa-bars" />
|
||||
</Dropdown.Toggle>
|
||||
<Dropdown.Menu className="dropdown-menu-right">
|
||||
<li>
|
||||
<a href="" onClick={this.showChangePassword.bind(this)}>
|
||||
Change Password <i className="fas fa-cog" />
|
||||
</a>
|
||||
{this.state.showChangePasswordModal && (
|
||||
<ChangePasswordModal
|
||||
serverInfo={serverInfo}
|
||||
hideChangePassword={this.hideChangePassword.bind(this)}
|
||||
/>
|
||||
)}
|
||||
</li>
|
||||
<li>
|
||||
<a target="_blank" href="https://docs.min.io/?ref=ob">
|
||||
Documentation <i className="fas fa-book" />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a target="_blank" href="https://github.com/minio/minio">
|
||||
GitHub <i className="fab fa-github" />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a target="_blank" href="https://min.io/pricing?ref=ob">
|
||||
Get Support <i className="fas fa-question-circle" />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="" id="show-about" onClick={this.showAbout.bind(this)}>
|
||||
About <i className="fas fa-info-circle" />
|
||||
</a>
|
||||
{this.state.showAboutModal && (
|
||||
<AboutModal
|
||||
serverInfo={serverInfo}
|
||||
hideAbout={this.hideAbout.bind(this)}
|
||||
/>
|
||||
)}
|
||||
</li>
|
||||
<li>
|
||||
<a href="" id="logout" onClick={this.logout}>
|
||||
Logout <i className="fas fa-sign-out-alt" />
|
||||
</a>
|
||||
</li>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
serverInfo: state.browser.serverInfo
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
fetchServerInfo: () => dispatch(browserActions.fetchServerInfo())
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(BrowserDropdown)
|
||||
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
import { connect } from "react-redux"
|
||||
import web from "../web"
|
||||
import * as alertActions from "../alert/actions"
|
||||
import { getRandomAccessKey, getRandomSecretKey } from "../utils"
|
||||
import jwtDecode from "jwt-decode"
|
||||
import classNames from "classnames"
|
||||
|
||||
import { Modal, ModalBody, ModalHeader } from "react-bootstrap"
|
||||
import InputGroup from "./InputGroup"
|
||||
import { ACCESS_KEY_MIN_LENGTH, SECRET_KEY_MIN_LENGTH } from "../constants"
|
||||
|
||||
export class ChangePasswordModal extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
currentAccessKey: "",
|
||||
currentSecretKey: "",
|
||||
currentSecretKeyVisible: false,
|
||||
newAccessKey: "",
|
||||
newSecretKey: "",
|
||||
newSecretKeyVisible: false
|
||||
}
|
||||
}
|
||||
// When its shown, it loads the access key from JWT token
|
||||
componentWillMount() {
|
||||
const token = jwtDecode(web.GetToken())
|
||||
this.setState({
|
||||
currentAccessKey: token.sub,
|
||||
newAccessKey: token.sub
|
||||
})
|
||||
}
|
||||
|
||||
// Save the auth params and set them.
|
||||
setAuth(e) {
|
||||
const { showAlert } = this.props
|
||||
|
||||
if (this.canUpdateCredentials()) {
|
||||
const currentAccessKey = this.state.currentAccessKey
|
||||
const currentSecretKey = this.state.currentSecretKey
|
||||
const newAccessKey = this.state.newAccessKey
|
||||
const newSecretKey = this.state.newSecretKey
|
||||
web
|
||||
.SetAuth({
|
||||
currentAccessKey,
|
||||
currentSecretKey,
|
||||
newAccessKey,
|
||||
newSecretKey
|
||||
})
|
||||
.then(data => {
|
||||
showAlert({
|
||||
type: "success",
|
||||
message: "Credentials updated successfully."
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
showAlert({
|
||||
type: "danger",
|
||||
message: err.message
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
generateAuth(e) {
|
||||
const { serverInfo } = this.props
|
||||
this.setState({
|
||||
newSecretKey: getRandomSecretKey(),
|
||||
newSecretKeyVisible: true
|
||||
})
|
||||
}
|
||||
|
||||
canChangePassword() {
|
||||
const { serverInfo } = this.props
|
||||
// Password change is not allowed for temporary users(STS)
|
||||
if(serverInfo.userInfo.isTempUser) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Password change is only allowed for regular users
|
||||
if (!serverInfo.userInfo.isIAMUser) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
canUpdateCredentials() {
|
||||
return (
|
||||
this.state.currentAccessKey.length > 0 &&
|
||||
this.state.currentSecretKey.length > 0 &&
|
||||
this.state.newAccessKey.length >= ACCESS_KEY_MIN_LENGTH &&
|
||||
this.state.newSecretKey.length >= SECRET_KEY_MIN_LENGTH
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
const { hideChangePassword, serverInfo } = this.props
|
||||
const allowChangePassword = this.canChangePassword()
|
||||
|
||||
if (!allowChangePassword) {
|
||||
return (
|
||||
<Modal bsSize="sm" animation={false} show={true}>
|
||||
<ModalHeader>Change Password</ModalHeader>
|
||||
<ModalBody>
|
||||
Credentials of this user cannot be updated through MinIO Browser.
|
||||
</ModalBody>
|
||||
<div className="modal-footer">
|
||||
<button
|
||||
id="cancel-change-password"
|
||||
className="btn btn-link"
|
||||
onClick={hideChangePassword}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal bsSize="sm" animation={false} show={true}>
|
||||
<ModalHeader>Change Password</ModalHeader>
|
||||
<ModalBody className="m-t-20">
|
||||
<div className="has-toggle-password">
|
||||
<InputGroup
|
||||
value={this.state.currentAccessKey}
|
||||
id="currentAccessKey"
|
||||
label="Current Access Key"
|
||||
name="currentAccesskey"
|
||||
type="text"
|
||||
spellCheck="false"
|
||||
required="required"
|
||||
autoComplete="false"
|
||||
align="ig-left"
|
||||
readonly={true}
|
||||
/>
|
||||
|
||||
<i
|
||||
onClick={() => {
|
||||
this.setState({
|
||||
currentSecretKeyVisible: !this.state.currentSecretKeyVisible
|
||||
})
|
||||
}}
|
||||
className={
|
||||
"toggle-password fas fa-eye " +
|
||||
(this.state.currentSecretKeyVisible ? "toggled" : "")
|
||||
}
|
||||
/>
|
||||
<InputGroup
|
||||
value={this.state.currentSecretKey}
|
||||
onChange={e => {
|
||||
this.setState({ currentSecretKey: e.target.value })
|
||||
}}
|
||||
id="currentSecretKey"
|
||||
label="Current Secret Key"
|
||||
name="currentSecretKey"
|
||||
type={this.state.currentSecretKeyVisible ? "text" : "password"}
|
||||
spellCheck="false"
|
||||
required="required"
|
||||
autoComplete="false"
|
||||
align="ig-left"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="has-toggle-password m-t-30">
|
||||
<i
|
||||
onClick={() => {
|
||||
this.setState({
|
||||
newSecretKeyVisible: !this.state.newSecretKeyVisible
|
||||
})
|
||||
}}
|
||||
className={
|
||||
"toggle-password fas fa-eye " +
|
||||
(this.state.newSecretKeyVisible ? "toggled" : "")
|
||||
}
|
||||
/>
|
||||
<InputGroup
|
||||
value={this.state.newSecretKey}
|
||||
onChange={e => {
|
||||
this.setState({ newSecretKey: e.target.value })
|
||||
}}
|
||||
id="newSecretKey"
|
||||
label="New Secret Key"
|
||||
name="newSecretKey"
|
||||
type={this.state.newSecretKeyVisible ? "text" : "password"}
|
||||
spellCheck="false"
|
||||
required="required"
|
||||
autoComplete="false"
|
||||
align="ig-left"
|
||||
onChange={e => {
|
||||
this.setState({ newSecretKey: e.target.value })
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</ModalBody>
|
||||
<div className="modal-footer">
|
||||
<button
|
||||
id="generate-keys"
|
||||
className={"btn btn-primary"}
|
||||
onClick={this.generateAuth.bind(this)}
|
||||
>
|
||||
Generate
|
||||
</button>
|
||||
<button
|
||||
id="update-keys"
|
||||
className={classNames({
|
||||
btn: true,
|
||||
"btn-success": this.canUpdateCredentials()
|
||||
})}
|
||||
disabled={!this.canUpdateCredentials()}
|
||||
onClick={this.setAuth.bind(this)}
|
||||
>
|
||||
Update
|
||||
</button>
|
||||
<button
|
||||
id="cancel-change-password"
|
||||
className="btn btn-link"
|
||||
onClick={hideChangePassword}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
serverInfo: state.browser.serverInfo
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
showAlert: alert => dispatch(alertActions.set(alert))
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ChangePasswordModal)
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
import { Modal, ModalBody } from "react-bootstrap"
|
||||
|
||||
let ConfirmModal = ({
|
||||
baseClass,
|
||||
icon,
|
||||
text,
|
||||
sub,
|
||||
okText,
|
||||
cancelText,
|
||||
okHandler,
|
||||
cancelHandler,
|
||||
show
|
||||
}) => {
|
||||
return (
|
||||
<Modal
|
||||
bsSize="small"
|
||||
animation={false}
|
||||
show={show}
|
||||
className={"modal-confirm " + (baseClass || "")}
|
||||
>
|
||||
<ModalBody>
|
||||
<div className="mc-icon">
|
||||
<i className={icon} />
|
||||
</div>
|
||||
<div className="mc-text">{text}</div>
|
||||
<div className="mc-sub">{sub}</div>
|
||||
</ModalBody>
|
||||
<div className="modal-footer">
|
||||
<button className="btn btn-danger" onClick={okHandler}>
|
||||
{okText}
|
||||
</button>
|
||||
<button className="btn btn-link" onClick={cancelHandler}>
|
||||
{cancelText}
|
||||
</button>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default ConfirmModal
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
import ObjectsSearch from "../objects/ObjectsSearch"
|
||||
import Path from "../objects/Path"
|
||||
import StorageInfo from "./StorageInfo"
|
||||
import BrowserDropdown from "./BrowserDropdown"
|
||||
import web from "../web"
|
||||
import { minioBrowserPrefix } from "../constants"
|
||||
|
||||
export const Header = () => {
|
||||
const loggedIn = web.LoggedIn()
|
||||
return (
|
||||
<header className="fe-header">
|
||||
<Path />
|
||||
{loggedIn && <StorageInfo />}
|
||||
{loggedIn && <ObjectsSearch />}
|
||||
<ul className="feh-actions">
|
||||
{loggedIn ? (
|
||||
<BrowserDropdown />
|
||||
) : (
|
||||
<a className="btn btn-danger" href={minioBrowserPrefix + "/login"}>
|
||||
Login
|
||||
</a>
|
||||
)}
|
||||
</ul>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
||||
export default Header
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
|
||||
export const Host = () => (
|
||||
<div className="fes-host">
|
||||
<i className="fas fa-globe-americas" />
|
||||
<a href="/">{window.location.host}</a>
|
||||
</div>
|
||||
)
|
||||
|
||||
export default Host
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
|
||||
let InputGroup = ({
|
||||
label,
|
||||
id,
|
||||
name,
|
||||
value,
|
||||
onChange,
|
||||
type,
|
||||
spellCheck,
|
||||
required,
|
||||
readonly,
|
||||
autoComplete,
|
||||
align,
|
||||
className
|
||||
}) => {
|
||||
var input = (
|
||||
<input
|
||||
id={id}
|
||||
name={name}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
className="ig-text"
|
||||
type={type}
|
||||
spellCheck={spellCheck}
|
||||
required={required}
|
||||
autoComplete={autoComplete}
|
||||
/>
|
||||
)
|
||||
if (readonly)
|
||||
input = (
|
||||
<input
|
||||
id={id}
|
||||
name={name}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
className="ig-text"
|
||||
type={type}
|
||||
spellCheck={spellCheck}
|
||||
required={required}
|
||||
autoComplete={autoComplete}
|
||||
disabled
|
||||
/>
|
||||
)
|
||||
return (
|
||||
<div className={"input-group " + align + " " + className}>
|
||||
{input}
|
||||
<i className="ig-helpers" />
|
||||
<label className="ig-label">{label}</label>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default InputGroup
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
import { connect } from "react-redux"
|
||||
import logo from "../../img/logo.svg"
|
||||
import Alert from "../alert/Alert"
|
||||
import * as actionsAlert from "../alert/actions"
|
||||
import InputGroup from "./InputGroup"
|
||||
import web from "../web"
|
||||
import { Redirect, Link } from "react-router-dom"
|
||||
import OpenIDLoginButton from './OpenIDLoginButton'
|
||||
|
||||
export class Login extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
accessKey: "",
|
||||
secretKey: "",
|
||||
discoveryDoc: {},
|
||||
clientId: ""
|
||||
}
|
||||
}
|
||||
|
||||
// Handle field changes
|
||||
accessKeyChange(e) {
|
||||
this.setState({
|
||||
accessKey: e.target.value
|
||||
})
|
||||
}
|
||||
|
||||
secretKeyChange(e) {
|
||||
this.setState({
|
||||
secretKey: e.target.value
|
||||
})
|
||||
}
|
||||
|
||||
handleSubmit(event) {
|
||||
event.preventDefault()
|
||||
const { showAlert, clearAlert, history } = this.props
|
||||
let message = ""
|
||||
if (this.state.accessKey === "") {
|
||||
message = "Access Key cannot be empty"
|
||||
}
|
||||
if (this.state.secretKey === "") {
|
||||
message = "Secret Key cannot be empty"
|
||||
}
|
||||
if (message) {
|
||||
showAlert("danger", message)
|
||||
return
|
||||
}
|
||||
web
|
||||
.Login({
|
||||
username: this.state.accessKey,
|
||||
password: this.state.secretKey
|
||||
})
|
||||
.then(res => {
|
||||
// Clear alerts from previous login attempts
|
||||
clearAlert()
|
||||
|
||||
history.push("/")
|
||||
})
|
||||
.catch(e => {
|
||||
showAlert("danger", e.message)
|
||||
})
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
const { clearAlert } = this.props
|
||||
// Clear out any stale message in the alert of previous page
|
||||
clearAlert()
|
||||
document.body.classList.add("is-guest")
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
web.GetDiscoveryDoc().then(({ DiscoveryDoc, clientId }) => {
|
||||
this.setState({
|
||||
clientId,
|
||||
discoveryDoc: DiscoveryDoc
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.body.classList.remove("is-guest")
|
||||
}
|
||||
|
||||
render() {
|
||||
const { clearAlert, alert } = this.props
|
||||
if (web.LoggedIn()) {
|
||||
return <Redirect to={"/"} />
|
||||
}
|
||||
let alertBox = <Alert {...alert} onDismiss={clearAlert} />
|
||||
// Make sure you don't show a fading out alert box on the initial web-page load.
|
||||
if (!alert.message) alertBox = ""
|
||||
|
||||
const showOpenID = Boolean(this.state.discoveryDoc && this.state.discoveryDoc.authorization_endpoint)
|
||||
return (
|
||||
<div className="login">
|
||||
{alertBox}
|
||||
<div className="l-wrap">
|
||||
<form onSubmit={this.handleSubmit.bind(this)}>
|
||||
<InputGroup
|
||||
value={this.state.accessKey}
|
||||
onChange={this.accessKeyChange.bind(this)}
|
||||
className="ig-dark"
|
||||
label="Access Key"
|
||||
id="accessKey"
|
||||
name="username"
|
||||
type="text"
|
||||
spellCheck="false"
|
||||
required="required"
|
||||
autoComplete="username"
|
||||
/>
|
||||
<InputGroup
|
||||
value={this.state.secretKey}
|
||||
onChange={this.secretKeyChange.bind(this)}
|
||||
className="ig-dark"
|
||||
label="Secret Key"
|
||||
id="secretKey"
|
||||
name="password"
|
||||
type="password"
|
||||
spellCheck="false"
|
||||
required="required"
|
||||
/>
|
||||
<button className="lw-btn" type="submit">
|
||||
<i className="fas fa-sign-in-alt" />
|
||||
</button>
|
||||
</form>
|
||||
{showOpenID && (
|
||||
<div className="openid-login">
|
||||
<div className="or">or</div>
|
||||
{
|
||||
this.state.clientId ? (
|
||||
<OpenIDLoginButton
|
||||
className="btn openid-btn"
|
||||
clientId={this.state.clientId}
|
||||
authEp={this.state.discoveryDoc.authorization_endpoint}
|
||||
authScopes={this.state.discoveryDoc.scopes_supported}
|
||||
>
|
||||
Log in with OpenID
|
||||
</OpenIDLoginButton>
|
||||
) : (
|
||||
<Link to={"/login/openid"} className="btn openid-btn">
|
||||
Log in with OpenID
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="l-footer">
|
||||
<a className="lf-logo" href="">
|
||||
<img src={logo} alt="" />
|
||||
</a>
|
||||
<div className="lf-server">{window.location.host}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
showAlert: (type, message) =>
|
||||
dispatch(actionsAlert.set({ type: type, message: message })),
|
||||
clearAlert: () => dispatch(actionsAlert.clear())
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(
|
||||
state => state,
|
||||
mapDispatchToProps
|
||||
)(Login)
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
import { connect } from "react-redux"
|
||||
import { Dropdown, OverlayTrigger, Tooltip } from "react-bootstrap"
|
||||
import web from "../web"
|
||||
import * as actionsBuckets from "../buckets/actions"
|
||||
import * as uploadsActions from "../uploads/actions"
|
||||
import { getPrefixWritable } from "../objects/selectors"
|
||||
|
||||
export const MainActions = ({
|
||||
prefixWritable,
|
||||
uploadFile,
|
||||
showMakeBucketModal
|
||||
}) => {
|
||||
const uploadTooltip = <Tooltip id="tt-upload-file">Upload file</Tooltip>
|
||||
const makeBucketTooltip = (
|
||||
<Tooltip id="tt-create-bucket">Create bucket</Tooltip>
|
||||
)
|
||||
const onFileUpload = e => {
|
||||
e.preventDefault()
|
||||
let files = e.target.files
|
||||
let filesToUploadCount = files.length
|
||||
for (let i = 0; i < filesToUploadCount; i++) {
|
||||
uploadFile(files.item(i))
|
||||
}
|
||||
e.target.value = null
|
||||
}
|
||||
|
||||
const loggedIn = web.LoggedIn()
|
||||
|
||||
if (loggedIn || prefixWritable) {
|
||||
return (
|
||||
<Dropdown dropup className="feb-actions" id="fe-action-toggle">
|
||||
<Dropdown.Toggle noCaret className="feba-toggle">
|
||||
<span>
|
||||
<i className="fas fa-plus" />
|
||||
</span>
|
||||
</Dropdown.Toggle>
|
||||
<Dropdown.Menu>
|
||||
<OverlayTrigger placement="left" overlay={uploadTooltip}>
|
||||
<a href="#" className="feba-btn feba-upload">
|
||||
<input
|
||||
type="file"
|
||||
onChange={onFileUpload}
|
||||
style={{ display: "none" }}
|
||||
id="file-input"
|
||||
multiple={true}
|
||||
/>
|
||||
<label htmlFor="file-input">
|
||||
{" "}
|
||||
<i className="fas fa-cloud-upload-alt" />{" "}
|
||||
</label>
|
||||
</a>
|
||||
</OverlayTrigger>
|
||||
{loggedIn && (
|
||||
<OverlayTrigger placement="left" overlay={makeBucketTooltip}>
|
||||
<a
|
||||
href="#"
|
||||
id="show-make-bucket"
|
||||
className="feba-btn feba-bucket"
|
||||
onClick={e => {
|
||||
e.preventDefault()
|
||||
showMakeBucketModal()
|
||||
}}
|
||||
>
|
||||
<i className="far fa-hdd" />
|
||||
</a>
|
||||
</OverlayTrigger>
|
||||
)}
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
)
|
||||
} else {
|
||||
return <noscript />
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
prefixWritable: getPrefixWritable(state)
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
uploadFile: file => dispatch(uploadsActions.uploadFile(file)),
|
||||
showMakeBucketModal: () => dispatch(actionsBuckets.showMakeBucketModal())
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(MainActions)
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
import MobileHeader from "./MobileHeader"
|
||||
import Header from "./Header"
|
||||
import ObjectsSection from "../objects/ObjectsSection"
|
||||
import MainActions from "./MainActions"
|
||||
import BucketPolicyModal from "../buckets/BucketPolicyModal"
|
||||
import MakeBucketModal from "../buckets/MakeBucketModal"
|
||||
import UploadModal from "../uploads/UploadModal"
|
||||
import ObjectsBulkActions from "../objects/ObjectsBulkActions"
|
||||
import Dropzone from "../uploads/Dropzone"
|
||||
|
||||
export const MainContent = () => (
|
||||
<div className="fe-body">
|
||||
<ObjectsBulkActions />
|
||||
<MobileHeader />
|
||||
<Dropzone>
|
||||
<Header />
|
||||
<ObjectsSection />
|
||||
</Dropzone>
|
||||
<MainActions />
|
||||
<BucketPolicyModal />
|
||||
<MakeBucketModal />
|
||||
<UploadModal />
|
||||
</div>
|
||||
)
|
||||
|
||||
export default MainContent
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
import classNames from "classnames"
|
||||
import { connect } from "react-redux"
|
||||
import logo from "../../img/logo.svg"
|
||||
import * as actionsCommon from "./actions"
|
||||
|
||||
export const MobileHeader = ({ sidebarOpen, toggleSidebar }) => (
|
||||
<header className="fe-header-mobile hidden-lg hidden-md">
|
||||
<div
|
||||
id="sidebar-toggle"
|
||||
className={
|
||||
"feh-trigger " +
|
||||
classNames({
|
||||
"feht-toggled": sidebarOpen
|
||||
})
|
||||
}
|
||||
onClick={e => {
|
||||
e.stopPropagation()
|
||||
toggleSidebar()
|
||||
}}
|
||||
>
|
||||
<div className="feht-lines">
|
||||
<div className="top" />
|
||||
<div className="center" />
|
||||
<div className="bottom" />
|
||||
</div>
|
||||
</div>
|
||||
<img className="mh-logo" src={logo} alt="" />
|
||||
</header>
|
||||
)
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
sidebarOpen: state.browser.sidebarOpen
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
toggleSidebar: () => dispatch(actionsCommon.toggleSidebar())
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(MobileHeader)
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
import { connect } from "react-redux"
|
||||
import logo from "../../img/logo.svg"
|
||||
import Alert from "../alert/Alert"
|
||||
import * as actionsAlert from "../alert/actions"
|
||||
import InputGroup from "./InputGroup"
|
||||
import web from "../web"
|
||||
import { Redirect } from "react-router-dom"
|
||||
import qs from "query-string"
|
||||
import { getRandomString } from "../utils"
|
||||
import storage from "local-storage-fallback"
|
||||
import jwtDecode from "jwt-decode"
|
||||
import { buildOpenIDAuthURL, OPEN_ID_NONCE_KEY } from './utils'
|
||||
|
||||
export class OpenIDLogin extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
clientID: "",
|
||||
discoveryDoc: {}
|
||||
}
|
||||
this.clientIDChange = this.clientIDChange.bind(this)
|
||||
this.handleSubmit = this.handleSubmit.bind(this)
|
||||
}
|
||||
|
||||
clientIDChange(e) {
|
||||
this.setState({
|
||||
clientID: e.target.value
|
||||
})
|
||||
}
|
||||
|
||||
handleSubmit(event) {
|
||||
event.preventDefault()
|
||||
const { showAlert } = this.props
|
||||
let message = ""
|
||||
if (this.state.clientID === "") {
|
||||
message = "Client ID cannot be empty"
|
||||
}
|
||||
if (message) {
|
||||
showAlert("danger", message)
|
||||
return
|
||||
}
|
||||
|
||||
if (this.state.discoveryDoc && this.state.discoveryDoc.authorization_endpoint) {
|
||||
const redirectURI = window.location.href.split("#")[0]
|
||||
|
||||
// Store nonce in localstorage to check again after the redirect
|
||||
const nonce = getRandomString(16)
|
||||
storage.setItem(OPEN_ID_NONCE_KEY, nonce)
|
||||
|
||||
const authURL = buildOpenIDAuthURL(
|
||||
this.state.discoveryDoc.authorization_endpoint,
|
||||
this.state.discoveryDoc.scopes_supported,
|
||||
redirectURI,
|
||||
this.state.clientID,
|
||||
nonce
|
||||
)
|
||||
window.location = authURL
|
||||
}
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
const { clearAlert } = this.props
|
||||
// Clear out any stale message in the alert of previous page
|
||||
clearAlert()
|
||||
document.body.classList.add("is-guest")
|
||||
|
||||
web.GetDiscoveryDoc().then(({ DiscoveryDoc }) => {
|
||||
this.setState({
|
||||
discoveryDoc: DiscoveryDoc
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const values = qs.parse(this.props.location.hash)
|
||||
if (values.error) {
|
||||
this.props.showAlert("danger", values.error_description)
|
||||
return
|
||||
}
|
||||
|
||||
if (values.id_token) {
|
||||
// Check nonce on the token to prevent replay attacks
|
||||
const tokenJSON = jwtDecode(values.id_token)
|
||||
if (storage.getItem(OPEN_ID_NONCE_KEY) !== tokenJSON.nonce) {
|
||||
this.props.showAlert("danger", "Invalid auth token")
|
||||
return
|
||||
}
|
||||
|
||||
web.LoginSTS({ token: values.id_token }).then(() => {
|
||||
storage.removeItem(OPEN_ID_NONCE_KEY)
|
||||
this.forceUpdate()
|
||||
return
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.body.classList.remove("is-guest")
|
||||
}
|
||||
|
||||
render() {
|
||||
const { clearAlert, alert } = this.props
|
||||
if (web.LoggedIn()) {
|
||||
return <Redirect to={"/"} />
|
||||
}
|
||||
let alertBox = <Alert {...alert} onDismiss={clearAlert} />
|
||||
// Make sure you don't show a fading out alert box on the initial web-page load.
|
||||
if (!alert.message) alertBox = ""
|
||||
return (
|
||||
<div className="login">
|
||||
{alertBox}
|
||||
<div className="l-wrap">
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<InputGroup
|
||||
value={this.state.clientID}
|
||||
onChange={this.clientIDChange}
|
||||
className="ig-dark"
|
||||
label="Client ID"
|
||||
id="clientID"
|
||||
name="clientID"
|
||||
type="text"
|
||||
spellCheck="false"
|
||||
required="required"
|
||||
/>
|
||||
<button className="lw-btn" type="submit">
|
||||
<i className="fas fa-sign-in-alt" />
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div className="l-footer">
|
||||
<a className="lf-logo" href="">
|
||||
<img src={logo} alt="" />
|
||||
</a>
|
||||
<div className="lf-server">{window.location.host}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
showAlert: (type, message) =>
|
||||
dispatch(actionsAlert.set({ type: type, message: message })),
|
||||
clearAlert: () => dispatch(actionsAlert.clear())
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(
|
||||
state => state,
|
||||
mapDispatchToProps
|
||||
)(OpenIDLogin)
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
import { getRandomString } from "../utils"
|
||||
import storage from "local-storage-fallback"
|
||||
import { buildOpenIDAuthURL, OPEN_ID_NONCE_KEY } from './utils'
|
||||
|
||||
export class OpenIDLoginButton extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.handleClick = this.handleClick.bind(this)
|
||||
}
|
||||
|
||||
handleClick(event) {
|
||||
event.stopPropagation()
|
||||
const { authEp, authScopes, clientId } = this.props
|
||||
|
||||
let redirectURI = window.location.href.split("#")[0]
|
||||
if (redirectURI.endsWith('/')) {
|
||||
redirectURI += 'openid'
|
||||
} else {
|
||||
redirectURI += '/openid'
|
||||
}
|
||||
|
||||
// Store nonce in localstorage to check again after the redirect
|
||||
const nonce = getRandomString(16)
|
||||
storage.setItem(OPEN_ID_NONCE_KEY, nonce)
|
||||
|
||||
const authURL = buildOpenIDAuthURL(authEp, authScopes, redirectURI, clientId, nonce)
|
||||
window.location = authURL
|
||||
}
|
||||
|
||||
render() {
|
||||
const { children, className } = this.props
|
||||
return (
|
||||
<div onClick={this.handleClick} className={className}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default OpenIDLoginButton
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
import classNames from "classnames"
|
||||
import ClickOutHandler from "react-onclickout"
|
||||
import { connect } from "react-redux"
|
||||
|
||||
import logo from "../../img/logo.svg"
|
||||
import BucketSearch from "../buckets/BucketSearch"
|
||||
import BucketList from "../buckets/BucketList"
|
||||
import Host from "./Host"
|
||||
import * as actionsCommon from "./actions"
|
||||
import web from "../web"
|
||||
|
||||
export const SideBar = ({ sidebarOpen, clickOutside }) => {
|
||||
const onClickOut = e => {
|
||||
if (e.target.classList.contains("feh-trigger")) {
|
||||
return
|
||||
}
|
||||
clickOutside()
|
||||
}
|
||||
return (
|
||||
<ClickOutHandler onClickOut={onClickOut}>
|
||||
<div
|
||||
className={classNames({
|
||||
"fe-sidebar": true,
|
||||
toggled: sidebarOpen
|
||||
})}
|
||||
>
|
||||
<div className="fes-header clearfix hidden-sm hidden-xs">
|
||||
<img src={logo} alt="" />
|
||||
<h2>MinIO Browser</h2>
|
||||
</div>
|
||||
<div className="fes-list">
|
||||
{web.LoggedIn() && <BucketSearch />}
|
||||
<BucketList />
|
||||
</div>
|
||||
<Host />
|
||||
</div>
|
||||
</ClickOutHandler>
|
||||
)
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
sidebarOpen: state.browser.sidebarOpen
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
clickOutside: () => dispatch(actionsCommon.closeSidebar())
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(SideBar)
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
import { connect } from "react-redux"
|
||||
import humanize from "humanize"
|
||||
import * as actionsCommon from "./actions"
|
||||
|
||||
export class StorageInfo extends React.Component {
|
||||
componentWillMount() {
|
||||
const { fetchStorageInfo } = this.props
|
||||
fetchStorageInfo()
|
||||
}
|
||||
render() {
|
||||
const { used } = this.props.storageInfo
|
||||
if (!used || used == 0) {
|
||||
return <noscript />
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="feh-used">
|
||||
<div className="fehu-chart">
|
||||
<div style={{ width: 0 }} />
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<span>Used: </span>
|
||||
{humanize.filesize(used)}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
storageInfo: state.browser.storageInfo
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
fetchStorageInfo: () => dispatch(actionsCommon.fetchStorageInfo())
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(StorageInfo)
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
import { shallow } from "enzyme"
|
||||
import { AboutModal } from "../AboutModal"
|
||||
|
||||
describe("AboutModal", () => {
|
||||
const serverInfo = {
|
||||
version: "test",
|
||||
platform: "test",
|
||||
runtime: "test"
|
||||
}
|
||||
|
||||
it("should render without crashing", () => {
|
||||
shallow(<AboutModal serverInfo={serverInfo} />)
|
||||
})
|
||||
|
||||
it("should call hideAbout when close button is clicked", () => {
|
||||
const hideAbout = jest.fn()
|
||||
const wrapper = shallow(
|
||||
<AboutModal serverInfo={serverInfo} hideAbout={hideAbout} />
|
||||
)
|
||||
wrapper.find("button").simulate("click")
|
||||
expect(hideAbout).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
import { shallow } from "enzyme"
|
||||
import Browser from "../Browser"
|
||||
import configureStore from "redux-mock-store"
|
||||
|
||||
const mockStore = configureStore()
|
||||
|
||||
describe("Browser", () => {
|
||||
it("should render without crashing", () => {
|
||||
const store = mockStore()
|
||||
shallow(<Browser store={store}/>)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
import { shallow } from "enzyme"
|
||||
import { BrowserDropdown } from "../BrowserDropdown"
|
||||
|
||||
describe("BrowserDropdown", () => {
|
||||
const serverInfo = {
|
||||
version: "test",
|
||||
platform: "test",
|
||||
runtime: "test"
|
||||
}
|
||||
|
||||
it("should render without crashing", () => {
|
||||
shallow(
|
||||
<BrowserDropdown serverInfo={serverInfo} fetchServerInfo={jest.fn()} />
|
||||
)
|
||||
})
|
||||
|
||||
it("should call fetchServerInfo after its mounted", () => {
|
||||
const fetchServerInfo = jest.fn()
|
||||
const wrapper = shallow(
|
||||
<BrowserDropdown
|
||||
serverInfo={serverInfo}
|
||||
fetchServerInfo={fetchServerInfo}
|
||||
/>
|
||||
)
|
||||
expect(fetchServerInfo).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("should show AboutModal when About link is clicked", () => {
|
||||
const wrapper = shallow(
|
||||
<BrowserDropdown serverInfo={serverInfo} fetchServerInfo={jest.fn()} />
|
||||
)
|
||||
wrapper.find("#show-about").simulate("click", { preventDefault: jest.fn() })
|
||||
wrapper.update()
|
||||
expect(wrapper.state("showAboutModal")).toBeTruthy()
|
||||
expect(wrapper.find("AboutModal").length).toBe(1)
|
||||
})
|
||||
|
||||
it("should logout and redirect to /login when logout is clicked", () => {
|
||||
const wrapper = shallow(
|
||||
<BrowserDropdown serverInfo={serverInfo} fetchServerInfo={jest.fn()} />
|
||||
)
|
||||
wrapper.find("#logout").simulate("click", { preventDefault: jest.fn() })
|
||||
expect(window.location.pathname.endsWith("/login")).toBeTruthy()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
import { shallow, mount } from "enzyme"
|
||||
import { ChangePasswordModal } from "../ChangePasswordModal"
|
||||
import jwtDecode from "jwt-decode"
|
||||
|
||||
jest.mock("jwt-decode")
|
||||
|
||||
jwtDecode.mockImplementation(() => ({ sub: "minio" }))
|
||||
|
||||
jest.mock("../../web", () => ({
|
||||
SetAuth: jest.fn(
|
||||
({ currentAccessKey, currentSecretKey, newAccessKey, newSecretKey }) => {
|
||||
if (
|
||||
currentAccessKey == "minio" &&
|
||||
currentSecretKey == "minio123" &&
|
||||
newAccessKey == "test" &&
|
||||
newSecretKey == "test1234"
|
||||
) {
|
||||
return Promise.resolve({})
|
||||
} else {
|
||||
return Promise.reject({
|
||||
message: "Error"
|
||||
})
|
||||
}
|
||||
}
|
||||
),
|
||||
GetToken: jest.fn(() => "")
|
||||
}))
|
||||
|
||||
jest.mock("../../utils", () => ({
|
||||
getRandomAccessKey: () => "raccesskey",
|
||||
getRandomSecretKey: () => "rsecretkey"
|
||||
}))
|
||||
|
||||
describe("ChangePasswordModal", () => {
|
||||
const serverInfo = {
|
||||
version: "test",
|
||||
platform: "test",
|
||||
runtime: "test",
|
||||
info: {},
|
||||
userInfo: { isIAMUser: true }
|
||||
}
|
||||
|
||||
it("should render without crashing", () => {
|
||||
shallow(<ChangePasswordModal serverInfo={serverInfo} />)
|
||||
})
|
||||
|
||||
it("should not allow changing password when not IAM user", () => {
|
||||
const newServerInfo = {
|
||||
...serverInfo,
|
||||
userInfo: { isIAMUser: false }
|
||||
}
|
||||
const wrapper = shallow(<ChangePasswordModal serverInfo={newServerInfo} />)
|
||||
expect(
|
||||
wrapper
|
||||
.find("ModalBody")
|
||||
.childAt(0)
|
||||
.text()
|
||||
).toBe("Credentials of this user cannot be updated through MinIO Browser.")
|
||||
})
|
||||
|
||||
it("should not allow changing password for STS user", () => {
|
||||
const newServerInfo = {
|
||||
...serverInfo,
|
||||
userInfo: { isTempUser: true }
|
||||
}
|
||||
const wrapper = shallow(<ChangePasswordModal serverInfo={newServerInfo} />)
|
||||
expect(
|
||||
wrapper
|
||||
.find("ModalBody")
|
||||
.childAt(0)
|
||||
.text()
|
||||
).toBe("Credentials of this user cannot be updated through MinIO Browser.")
|
||||
})
|
||||
|
||||
it("should not generate accessKey for IAM User", () => {
|
||||
const wrapper = shallow(<ChangePasswordModal serverInfo={serverInfo} />)
|
||||
wrapper.find("#generate-keys").simulate("click")
|
||||
setImmediate(() => {
|
||||
expect(wrapper.state("newAccessKey")).toBe("minio")
|
||||
expect(wrapper.state("newSecretKey")).toBe("rsecretkey")
|
||||
})
|
||||
})
|
||||
|
||||
it("should not show new accessKey field for IAM User", () => {
|
||||
const wrapper = shallow(<ChangePasswordModal serverInfo={serverInfo} />)
|
||||
expect(wrapper.find("#newAccesskey").exists()).toBeFalsy()
|
||||
})
|
||||
|
||||
it("should disable Update button for secretKey", () => {
|
||||
const showAlert = jest.fn()
|
||||
const wrapper = shallow(
|
||||
<ChangePasswordModal serverInfo={serverInfo} showAlert={showAlert} />
|
||||
)
|
||||
wrapper
|
||||
.find("#currentSecretKey")
|
||||
.simulate("change", { target: { value: "minio123" } })
|
||||
wrapper
|
||||
.find("#newSecretKey")
|
||||
.simulate("change", { target: { value: "t1" } })
|
||||
expect(wrapper.find("#update-keys").prop("disabled")).toBeTruthy()
|
||||
})
|
||||
|
||||
it("should call hideChangePassword when Cancel button is clicked", () => {
|
||||
const hideChangePassword = jest.fn()
|
||||
const wrapper = shallow(
|
||||
<ChangePasswordModal
|
||||
serverInfo={serverInfo}
|
||||
hideChangePassword={hideChangePassword}
|
||||
/>
|
||||
)
|
||||
wrapper.find("#cancel-change-password").simulate("click")
|
||||
expect(hideChangePassword).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
import { shallow } from "enzyme"
|
||||
import Header from "../Header"
|
||||
|
||||
jest.mock("../../web", () => ({
|
||||
LoggedIn: jest
|
||||
.fn(() => true)
|
||||
.mockReturnValueOnce(true)
|
||||
.mockReturnValueOnce(false)
|
||||
}))
|
||||
describe("Header", () => {
|
||||
it("should render without crashing", () => {
|
||||
shallow(<Header />)
|
||||
})
|
||||
|
||||
it("should render Login button when the user has not LoggedIn", () => {
|
||||
const wrapper = shallow(<Header />)
|
||||
expect(wrapper.find("a").text()).toBe("Login")
|
||||
})
|
||||
|
||||
it("should render StorageInfo and BrowserDropdown when the user has LoggedIn", () => {
|
||||
const wrapper = shallow(<Header />)
|
||||
expect(wrapper.find("Connect(BrowserDropdown)").length).toBe(1)
|
||||
expect(wrapper.find("Connect(StorageInfo)").length).toBe(1)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
import { shallow } from "enzyme"
|
||||
import Host from "../Host"
|
||||
|
||||
describe("Host", () => {
|
||||
it("should render without crashing", () => {
|
||||
shallow(<Host />)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
import { shallow, mount } from "enzyme"
|
||||
import { Login } from "../Login"
|
||||
import web from "../../web"
|
||||
|
||||
jest.mock("../../web", () => ({
|
||||
Login: jest.fn(() => {
|
||||
return Promise.resolve({ token: "test", uiVersion: "2018-02-01T01:17:47Z" })
|
||||
}),
|
||||
LoggedIn: jest.fn(),
|
||||
GetDiscoveryDoc: jest.fn(() => {
|
||||
return Promise.resolve({ DiscoveryDoc: {"authorization_endpoint": "test"} })
|
||||
})
|
||||
}))
|
||||
|
||||
describe("Login", () => {
|
||||
const dispatchMock = jest.fn()
|
||||
const showAlertMock = jest.fn()
|
||||
const clearAlertMock = jest.fn()
|
||||
|
||||
it("should render without crashing", () => {
|
||||
shallow(<Login
|
||||
dispatch={dispatchMock}
|
||||
alert={{ show: false, type: "danger"}}
|
||||
showAlert={showAlertMock}
|
||||
clearAlert={clearAlertMock}
|
||||
/>)
|
||||
})
|
||||
|
||||
it("should initially have the is-guest class", () => {
|
||||
const wrapper = shallow(
|
||||
<Login
|
||||
dispatch={dispatchMock}
|
||||
alert={{ show: false, type: "danger"}}
|
||||
showAlert={showAlertMock}
|
||||
clearAlert={clearAlertMock}
|
||||
/>,
|
||||
{ attachTo: document.body }
|
||||
)
|
||||
expect(document.body.classList.contains("is-guest")).toBeTruthy()
|
||||
})
|
||||
|
||||
it("should throw an alert if the keys are empty in login form", () => {
|
||||
const wrapper = mount(
|
||||
<Login
|
||||
dispatch={dispatchMock}
|
||||
alert={{ show: false, type: "danger"}}
|
||||
showAlert={showAlertMock}
|
||||
clearAlert={clearAlertMock}
|
||||
/>
|
||||
)
|
||||
// case where both keys are empty - displays the second warning
|
||||
wrapper.find("form").simulate("submit")
|
||||
expect(showAlertMock).toHaveBeenCalledWith("danger", "Secret Key cannot be empty")
|
||||
|
||||
// case where access key is empty
|
||||
wrapper.setState({
|
||||
accessKey: "",
|
||||
secretKey: "secretKey"
|
||||
})
|
||||
wrapper.find("form").simulate("submit")
|
||||
expect(showAlertMock).toHaveBeenCalledWith("danger", "Access Key cannot be empty")
|
||||
|
||||
// case where secret key is empty
|
||||
wrapper.setState({
|
||||
accessKey: "accessKey",
|
||||
secretKey: ""
|
||||
})
|
||||
wrapper.find("form").simulate("submit")
|
||||
expect(showAlertMock).toHaveBeenCalledWith("danger", "Secret Key cannot be empty")
|
||||
})
|
||||
|
||||
it("should call web.Login with correct arguments if both keys are entered", () => {
|
||||
const wrapper = mount(
|
||||
<Login
|
||||
dispatch={dispatchMock}
|
||||
alert={{ show: false, type: "danger"}}
|
||||
showAlert={showAlertMock}
|
||||
clearAlert={clearAlertMock}
|
||||
/>
|
||||
)
|
||||
wrapper.setState({
|
||||
accessKey: "accessKey",
|
||||
secretKey: "secretKey"
|
||||
})
|
||||
wrapper.find("form").simulate("submit")
|
||||
expect(web.Login).toHaveBeenCalledWith({
|
||||
"username": "accessKey",
|
||||
"password": "secretKey"
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
import { shallow, mount } from "enzyme"
|
||||
import { MainActions } from "../MainActions"
|
||||
|
||||
jest.mock("../../web", () => ({
|
||||
LoggedIn: jest
|
||||
.fn(() => true)
|
||||
.mockReturnValueOnce(true)
|
||||
.mockReturnValueOnce(false)
|
||||
.mockReturnValueOnce(false)
|
||||
}))
|
||||
|
||||
describe("MainActions", () => {
|
||||
it("should render without crashing", () => {
|
||||
shallow(<MainActions />)
|
||||
})
|
||||
|
||||
it("should not show any actions when user has not LoggedIn and prefixWritable is false", () => {
|
||||
const wrapper = shallow(<MainActions />)
|
||||
expect(wrapper.find("#show-make-bucket").length).toBe(0)
|
||||
expect(wrapper.find("#file-input").length).toBe(0)
|
||||
})
|
||||
|
||||
it("should show only file upload action when user has not LoggedIn and prefixWritable is true", () => {
|
||||
const wrapper = shallow(<MainActions prefixWritable={true} />)
|
||||
expect(wrapper.find("#show-make-bucket").length).toBe(0)
|
||||
expect(wrapper.find("#file-input").length).toBe(1)
|
||||
})
|
||||
|
||||
it("should show make bucket upload file actions when user has LoggedIn", () => {
|
||||
const wrapper = shallow(<MainActions />)
|
||||
expect(wrapper.find("#show-make-bucket").length).toBe(1)
|
||||
expect(wrapper.find("#file-input").length).toBe(1)
|
||||
})
|
||||
|
||||
it("should call showMakeBucketModal when create bucket icon is clicked", () => {
|
||||
const showMakeBucketModal = jest.fn()
|
||||
const wrapper = shallow(
|
||||
<MainActions showMakeBucketModal={showMakeBucketModal} />
|
||||
)
|
||||
wrapper
|
||||
.find("#show-make-bucket")
|
||||
.simulate("click", { preventDefault: jest.fn() })
|
||||
expect(showMakeBucketModal).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("should call uploadFile when a file is selected for upload", () => {
|
||||
const uploadFile = jest.fn()
|
||||
const wrapper = shallow(<MainActions uploadFile={uploadFile} />)
|
||||
const files = [new Blob(["file content"], { type: "text/plain" })]
|
||||
const input = wrapper.find("#file-input")
|
||||
const event = {
|
||||
preventDefault: jest.fn(),
|
||||
target: {
|
||||
files: {
|
||||
length: files.length,
|
||||
item: function(index) {
|
||||
return files[index]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
input.simulate("change", event)
|
||||
expect(uploadFile).toHaveBeenCalledWith(files[0])
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
import { shallow } from "enzyme"
|
||||
import MainContent from "../MainContent"
|
||||
|
||||
describe("MainContent", () => {
|
||||
it("should render without crashing", () => {
|
||||
shallow(<MainContent />)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
import { shallow } from "enzyme"
|
||||
import { MobileHeader } from "../MobileHeader"
|
||||
|
||||
describe("Bucket", () => {
|
||||
it("should render without crashing", () => {
|
||||
shallow(<MobileHeader sidebarOpen={false} />)
|
||||
})
|
||||
|
||||
it("should toggleSidebar when trigger is clicked", () => {
|
||||
const toggleSidebar = jest.fn()
|
||||
const wrapper = shallow(
|
||||
<MobileHeader sidebarOpen={false} toggleSidebar={toggleSidebar} />
|
||||
)
|
||||
wrapper
|
||||
.find("#sidebar-toggle")
|
||||
.simulate("click", { stopPropagation: jest.fn() })
|
||||
expect(toggleSidebar).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
import { shallow } from "enzyme"
|
||||
import { SideBar } from "../SideBar"
|
||||
|
||||
jest.mock("../../web", () => ({
|
||||
LoggedIn: jest.fn(() => false).mockReturnValueOnce(true)
|
||||
}))
|
||||
|
||||
describe("SideBar", () => {
|
||||
it("should render without crashing", () => {
|
||||
shallow(<SideBar />)
|
||||
})
|
||||
|
||||
it("should not render BucketSearch for non LoggedIn users", () => {
|
||||
const wrapper = shallow(<SideBar />)
|
||||
expect(wrapper.find("Connect(BucketSearch)").length).toBe(0)
|
||||
})
|
||||
|
||||
it("should call clickOutside when the user clicks outside the sidebar", () => {
|
||||
const clickOutside = jest.fn()
|
||||
const wrapper = shallow(<SideBar clickOutside={clickOutside} />)
|
||||
wrapper.simulate("clickOut", {
|
||||
preventDefault: jest.fn(),
|
||||
target: { classList: { contains: jest.fn(() => false) } }
|
||||
})
|
||||
expect(clickOutside).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("should not call clickOutside when user clicks on sidebar toggle", () => {
|
||||
const clickOutside = jest.fn()
|
||||
const wrapper = shallow(<SideBar clickOutside={clickOutside} />)
|
||||
wrapper.simulate("clickOut", {
|
||||
preventDefault: jest.fn(),
|
||||
target: { classList: { contains: jest.fn(() => true) } }
|
||||
})
|
||||
expect(clickOutside).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react"
|
||||
import { shallow } from "enzyme"
|
||||
import { StorageInfo } from "../StorageInfo"
|
||||
|
||||
describe("StorageInfo", () => {
|
||||
it("should render without crashing", () => {
|
||||
shallow(
|
||||
<StorageInfo storageInfo={ {used: 60} } fetchStorageInfo={jest.fn()} />
|
||||
)
|
||||
})
|
||||
|
||||
it("should fetchStorageInfo before component is mounted", () => {
|
||||
const fetchStorageInfo = jest.fn()
|
||||
shallow(
|
||||
<StorageInfo
|
||||
storageInfo={ {used: 60} }
|
||||
fetchStorageInfo={fetchStorageInfo}
|
||||
/>
|
||||
)
|
||||
expect(fetchStorageInfo).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("should not render anything if used is null", () => {
|
||||
const fetchStorageInfo = jest.fn()
|
||||
const wrapper = shallow(
|
||||
<StorageInfo
|
||||
storageInfo={ {used: 0} }
|
||||
fetchStorageInfo={fetchStorageInfo}
|
||||
/>
|
||||
)
|
||||
expect(wrapper.text()).toBe("")
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import configureStore from "redux-mock-store"
|
||||
import thunk from "redux-thunk"
|
||||
import * as actionsCommon from "../actions"
|
||||
|
||||
jest.mock("../../web", () => ({
|
||||
StorageInfo: jest.fn(() => {
|
||||
return Promise.resolve({
|
||||
used: 60
|
||||
})
|
||||
}),
|
||||
ServerInfo: jest.fn(() => {
|
||||
return Promise.resolve({
|
||||
MinioVersion: "test",
|
||||
MinioPlatform: "test",
|
||||
MinioRuntime: "test",
|
||||
MinioGlobalInfo: "test"
|
||||
})
|
||||
})
|
||||
}))
|
||||
|
||||
const middlewares = [thunk]
|
||||
const mockStore = configureStore(middlewares)
|
||||
|
||||
describe("Common actions", () => {
|
||||
it("creates common/SET_STORAGE_INFO after fetching the storage details ", () => {
|
||||
const store = mockStore()
|
||||
const expectedActions = [
|
||||
{ type: "common/SET_STORAGE_INFO", storageInfo: { used: 60 } }
|
||||
]
|
||||
return store.dispatch(actionsCommon.fetchStorageInfo()).then(() => {
|
||||
const actions = store.getActions()
|
||||
expect(actions).toEqual(expectedActions)
|
||||
})
|
||||
})
|
||||
|
||||
it("creates common/SET_SERVER_INFO after fetching the server details", () => {
|
||||
const store = mockStore()
|
||||
const expectedActions = [
|
||||
{
|
||||
type: "common/SET_SERVER_INFO",
|
||||
serverInfo: {
|
||||
version: "test",
|
||||
platform: "test",
|
||||
runtime: "test",
|
||||
info: "test"
|
||||
}
|
||||
}
|
||||
]
|
||||
return store.dispatch(actionsCommon.fetchServerInfo()).then(() => {
|
||||
const actions = store.getActions()
|
||||
expect(actions).toEqual(expectedActions)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import reducer from "../reducer"
|
||||
import * as actionsCommon from "../actions"
|
||||
|
||||
describe("common reducer", () => {
|
||||
it("should return the initial state", () => {
|
||||
expect(reducer(undefined, {})).toEqual({
|
||||
sidebarOpen: false,
|
||||
storageInfo: {used: 0},
|
||||
serverInfo: {}
|
||||
})
|
||||
})
|
||||
|
||||
it("should handle TOGGLE_SIDEBAR", () => {
|
||||
expect(
|
||||
reducer(
|
||||
{ sidebarOpen: false },
|
||||
{
|
||||
type: actionsCommon.TOGGLE_SIDEBAR
|
||||
}
|
||||
)
|
||||
).toEqual({
|
||||
sidebarOpen: true
|
||||
})
|
||||
})
|
||||
|
||||
it("should handle CLOSE_SIDEBAR", () => {
|
||||
expect(
|
||||
reducer(
|
||||
{ sidebarOpen: true },
|
||||
{
|
||||
type: actionsCommon.CLOSE_SIDEBAR
|
||||
}
|
||||
)
|
||||
).toEqual({
|
||||
sidebarOpen: false
|
||||
})
|
||||
})
|
||||
|
||||
it("should handle SET_STORAGE_INFO", () => {
|
||||
expect(
|
||||
reducer(
|
||||
{},
|
||||
{
|
||||
type: actionsCommon.SET_STORAGE_INFO,
|
||||
storageInfo: { }
|
||||
}
|
||||
)
|
||||
).toEqual({
|
||||
storageInfo: { }
|
||||
})
|
||||
})
|
||||
|
||||
it("should handle SET_SERVER_INFO", () => {
|
||||
expect(
|
||||
reducer(undefined, {
|
||||
type: actionsCommon.SET_SERVER_INFO,
|
||||
serverInfo: {
|
||||
version: "test",
|
||||
platform: "test",
|
||||
runtime: "test",
|
||||
info: "test"
|
||||
}
|
||||
}).serverInfo
|
||||
).toEqual({
|
||||
version: "test",
|
||||
platform: "test",
|
||||
runtime: "test",
|
||||
info: "test"
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import web from "../web"
|
||||
|
||||
export const TOGGLE_SIDEBAR = "common/TOGGLE_SIDEBAR"
|
||||
export const CLOSE_SIDEBAR = "common/CLOSE_SIDEBAR"
|
||||
export const SET_STORAGE_INFO = "common/SET_STORAGE_INFO"
|
||||
export const SET_SERVER_INFO = "common/SET_SERVER_INFO"
|
||||
|
||||
export const toggleSidebar = () => ({
|
||||
type: TOGGLE_SIDEBAR
|
||||
})
|
||||
|
||||
export const closeSidebar = () => ({
|
||||
type: CLOSE_SIDEBAR
|
||||
})
|
||||
|
||||
export const fetchStorageInfo = () => {
|
||||
return function(dispatch) {
|
||||
return web.StorageInfo().then(res => {
|
||||
const storageInfo = {
|
||||
used: res.used
|
||||
}
|
||||
dispatch(setStorageInfo(storageInfo))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const setStorageInfo = storageInfo => ({
|
||||
type: SET_STORAGE_INFO,
|
||||
storageInfo
|
||||
})
|
||||
|
||||
export const fetchServerInfo = () => {
|
||||
return function(dispatch) {
|
||||
return web.ServerInfo().then(res => {
|
||||
const serverInfo = {
|
||||
version: res.MinioVersion,
|
||||
platform: res.MinioPlatform,
|
||||
runtime: res.MinioRuntime,
|
||||
info: res.MinioGlobalInfo,
|
||||
userInfo: res.MinioUserInfo
|
||||
}
|
||||
dispatch(setServerInfo(serverInfo))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const setServerInfo = serverInfo => ({
|
||||
type: SET_SERVER_INFO,
|
||||
serverInfo
|
||||
})
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as actionsCommon from "./actions"
|
||||
|
||||
export default (
|
||||
state = {
|
||||
sidebarOpen: false,
|
||||
storageInfo: {used: 0},
|
||||
serverInfo: {}
|
||||
},
|
||||
action
|
||||
) => {
|
||||
switch (action.type) {
|
||||
case actionsCommon.TOGGLE_SIDEBAR:
|
||||
return Object.assign({}, state, {
|
||||
sidebarOpen: !state.sidebarOpen
|
||||
})
|
||||
case actionsCommon.CLOSE_SIDEBAR:
|
||||
return Object.assign({}, state, {
|
||||
sidebarOpen: false
|
||||
})
|
||||
case actionsCommon.SET_STORAGE_INFO:
|
||||
return Object.assign({}, state, {
|
||||
storageInfo: action.storageInfo
|
||||
})
|
||||
case actionsCommon.SET_SERVER_INFO:
|
||||
return { ...state, serverInfo: action.serverInfo }
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createSelector } from "reselect"
|
||||
|
||||
export const getServerInfo = state => state.browser.serverInfo
|
||||
|
||||
export const hasServerPublicDomain = createSelector(
|
||||
getServerInfo,
|
||||
serverInfo => Boolean(serverInfo.info && serverInfo.info.domains && serverInfo.info.domains.length),
|
||||
)
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* MinIO Object Storage (c) 2021 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user