Changed Share Object logic to use Access Keys (#2827)
Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
@@ -461,12 +461,24 @@ func ListObjects(bucketName, prefix, withVersions string) (*http.Response, error
|
||||
return response, err
|
||||
}
|
||||
|
||||
func SharesAnObjectOnAUrl(bucketName, prefix, versionID, expires string) (*http.Response, error) {
|
||||
// Helper function to share an object on a url
|
||||
func SharesAnObjectOnAUrl(bucketName, prefix, versionID, expires, accessKey, secretKey string) (*http.Response, error) {
|
||||
// Helper function to share an object on an url
|
||||
|
||||
requestDataAdd := map[string]interface{}{
|
||||
"prefix": prefix,
|
||||
"version_id": versionID,
|
||||
"expires": expires,
|
||||
"access_key": accessKey,
|
||||
"secret_key": secretKey,
|
||||
}
|
||||
|
||||
requestDataJSON, _ := json.Marshal(requestDataAdd)
|
||||
requestDataBody := bytes.NewReader(requestDataJSON)
|
||||
|
||||
request, err := http.NewRequest(
|
||||
"GET",
|
||||
"http://localhost:9090/api/v1/buckets/"+bucketName+"/objects/share?prefix="+prefix+"&version_id="+versionID+"&expires="+expires,
|
||||
nil,
|
||||
"POST",
|
||||
"http://localhost:9090/api/v1/buckets/"+bucketName+"/objects/share",
|
||||
requestDataBody,
|
||||
)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
@@ -743,6 +755,39 @@ func PutObjectsLegalholdStatus(bucketName, prefix, status, versionID string) (*h
|
||||
return response, err
|
||||
}
|
||||
|
||||
func PostServiceAccountCredentials(accessKey, secretKey, policy string) (*http.Response, error) {
|
||||
/*
|
||||
Helper function to create a service account
|
||||
POST: {{baseUrl}}/service-account-credentials
|
||||
{
|
||||
"accessKey":"testsa",
|
||||
"secretKey":"secretsa",
|
||||
"policy":""
|
||||
}
|
||||
*/
|
||||
requestDataAdd := map[string]interface{}{
|
||||
"accessKey": accessKey,
|
||||
"secretKey": secretKey,
|
||||
"policy": policy,
|
||||
}
|
||||
requestDataJSON, _ := json.Marshal(requestDataAdd)
|
||||
requestDataBody := bytes.NewReader(requestDataJSON)
|
||||
|
||||
request, err := http.NewRequest("POST",
|
||||
"http://localhost:9090/api/v1/service-account-credentials",
|
||||
requestDataBody)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
client := &http.Client{
|
||||
Timeout: 2 * time.Second,
|
||||
}
|
||||
response, err := client.Do(request)
|
||||
return response, err
|
||||
}
|
||||
|
||||
func TestPutObjectsLegalholdStatus(t *testing.T) {
|
||||
// Variables
|
||||
assert := assert.New(t)
|
||||
@@ -1514,6 +1559,8 @@ func TestShareObjectOnURL(t *testing.T) {
|
||||
tags := make(map[string]string)
|
||||
tags["tag"] = "testputobjecttagbucketonetagone"
|
||||
versionID := "null"
|
||||
accessKey := "testaccesskey"
|
||||
secretKey := "secretAccessKey"
|
||||
|
||||
// 1. Create the bucket
|
||||
if !setupBucket(bucketName, false, false, nil, nil, assert, 200) {
|
||||
@@ -1534,6 +1581,21 @@ func TestShareObjectOnURL(t *testing.T) {
|
||||
inspectHTTPResponse(uploadResponse),
|
||||
)
|
||||
}
|
||||
// 2. Create Access Key
|
||||
accKeyRsp, createError := PostServiceAccountCredentials(accessKey, secretKey, "")
|
||||
|
||||
if createError != nil {
|
||||
log.Println(createError)
|
||||
return
|
||||
}
|
||||
|
||||
if accKeyRsp != nil {
|
||||
assert.Equal(
|
||||
201,
|
||||
accKeyRsp.StatusCode,
|
||||
inspectHTTPResponse(accKeyRsp),
|
||||
)
|
||||
}
|
||||
|
||||
type args struct {
|
||||
prefix string
|
||||
@@ -1561,7 +1623,7 @@ func TestShareObjectOnURL(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// 3. Share the object on a URL
|
||||
shareResponse, shareError := SharesAnObjectOnAUrl(bucketName, tt.args.prefix, versionID, "604800s")
|
||||
shareResponse, shareError := SharesAnObjectOnAUrl(bucketName, tt.args.prefix, versionID, "604800s", accessKey, secretKey)
|
||||
assert.Nil(shareError)
|
||||
if shareError != nil {
|
||||
log.Println(shareError)
|
||||
|
||||
142
models/share_request.go
Normal file
142
models/share_request.go
Normal file
@@ -0,0 +1,142 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/go-openapi/validate"
|
||||
)
|
||||
|
||||
// ShareRequest share request
|
||||
//
|
||||
// swagger:model shareRequest
|
||||
type ShareRequest struct {
|
||||
|
||||
// access key
|
||||
// Required: true
|
||||
AccessKey *string `json:"access_key"`
|
||||
|
||||
// expires
|
||||
Expires string `json:"expires,omitempty"`
|
||||
|
||||
// prefix
|
||||
// Required: true
|
||||
Prefix *string `json:"prefix"`
|
||||
|
||||
// secret key
|
||||
// Required: true
|
||||
SecretKey *string `json:"secret_key"`
|
||||
|
||||
// version id
|
||||
// Required: true
|
||||
VersionID *string `json:"version_id"`
|
||||
}
|
||||
|
||||
// Validate validates this share request
|
||||
func (m *ShareRequest) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateAccessKey(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validatePrefix(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateSecretKey(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateVersionID(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ShareRequest) validateAccessKey(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("access_key", "body", m.AccessKey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ShareRequest) validatePrefix(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("prefix", "body", m.Prefix); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ShareRequest) validateSecretKey(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("secret_key", "body", m.SecretKey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ShareRequest) validateVersionID(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("version_id", "body", m.VersionID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this share request based on context it is used
|
||||
func (m *ShareRequest) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *ShareRequest) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *ShareRequest) UnmarshalBinary(b []byte) error {
|
||||
var res ShareRequest
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
@@ -66,7 +66,7 @@
|
||||
},
|
||||
"proxy": "http://localhost:9090/",
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.32.3",
|
||||
"@playwright/test": "^1.34.0",
|
||||
"@types/lodash": "^4.14.194",
|
||||
"@types/luxon": "^3.3.0",
|
||||
"@types/minio": "^7.0.18",
|
||||
@@ -85,6 +85,7 @@
|
||||
"@types/websocket": "^1.0.0",
|
||||
"babel-plugin-istanbul": "^6.1.1",
|
||||
"customize-cra": "^1.0.0",
|
||||
"minio": "^7.0.33",
|
||||
"nyc": "^15.1.0",
|
||||
"playwright": "^1.31.3",
|
||||
"prettier": "2.8.8",
|
||||
@@ -92,8 +93,7 @@
|
||||
"react-app-rewired": "^2.2.1",
|
||||
"react-scripts": "5.0.1",
|
||||
"testcafe": "^2.5.0",
|
||||
"typescript": "^4.4.3",
|
||||
"minio": "^7.0.33"
|
||||
"typescript": "^4.4.3"
|
||||
},
|
||||
"resolutions": {
|
||||
"nth-check": "^2.0.1",
|
||||
|
||||
@@ -1494,6 +1494,14 @@ export interface LdapPolicyEntity {
|
||||
groups?: string[];
|
||||
}
|
||||
|
||||
export interface ShareRequest {
|
||||
prefix: string;
|
||||
version_id: string;
|
||||
expires?: string;
|
||||
access_key: string;
|
||||
secret_key: string;
|
||||
}
|
||||
|
||||
export type QueryParamsType = Record<string | number, any>;
|
||||
export type ResponseFormat = keyof Omit<Body, "body" | "bodyUsed">;
|
||||
|
||||
@@ -2171,23 +2179,20 @@ export class Api<
|
||||
* @tags Object
|
||||
* @name ShareObject
|
||||
* @summary Shares an Object on a url
|
||||
* @request GET:/buckets/{bucket_name}/objects/share
|
||||
* @request POST:/buckets/{bucket_name}/objects/share
|
||||
* @secure
|
||||
*/
|
||||
shareObject: (
|
||||
bucketName: string,
|
||||
query: {
|
||||
prefix: string;
|
||||
version_id: string;
|
||||
expires?: string;
|
||||
},
|
||||
body: ShareRequest,
|
||||
params: RequestParams = {}
|
||||
) =>
|
||||
this.request<IamEntity, Error>({
|
||||
path: `/buckets/${bucketName}/objects/share`,
|
||||
method: "GET",
|
||||
query: query,
|
||||
method: "POST",
|
||||
body: body,
|
||||
secure: true,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
}),
|
||||
|
||||
@@ -17,11 +17,20 @@
|
||||
import React, { Fragment, useEffect, useState } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import { Button, CopyIcon, ReadBox, ShareIcon } from "mds";
|
||||
import {
|
||||
Button,
|
||||
CopyIcon,
|
||||
FormLayout,
|
||||
Grid,
|
||||
InputBox,
|
||||
RadioGroup,
|
||||
ReadBox,
|
||||
Select,
|
||||
ShareIcon,
|
||||
} from "mds";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
import CopyToClipboard from "react-copy-to-clipboard";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import LinearProgress from "@mui/material/LinearProgress";
|
||||
import {
|
||||
formFieldStyles,
|
||||
@@ -36,10 +45,13 @@ import DaysSelector from "../../../../Common/FormComponents/DaysSelector/DaysSel
|
||||
import { encodeURLString } from "../../../../../../common/utils";
|
||||
import {
|
||||
selDistSet,
|
||||
setErrorSnackMessage,
|
||||
setModalErrorSnackMessage,
|
||||
setModalSnackMessage,
|
||||
} from "../../../../../../systemSlice";
|
||||
import { useAppDispatch } from "../../../../../../store";
|
||||
import { DateTime } from "luxon";
|
||||
import { stringSort } from "../../../../../../utils/sortFunctions";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -85,11 +97,17 @@ const ShareFile = ({
|
||||
const dispatch = useAppDispatch();
|
||||
const distributedSetup = useSelector(selDistSet);
|
||||
const [shareURL, setShareURL] = useState<string>("");
|
||||
const [isLoadingVersion, setIsLoadingVersion] = useState<boolean>(true);
|
||||
const [isLoadingFile, setIsLoadingFile] = useState<boolean>(false);
|
||||
const [isLoadingURL, setIsLoadingURL] = useState<boolean>(false);
|
||||
const [isLoadingAccessKeys, setLoadingAccessKeys] = useState<boolean>(true);
|
||||
const [selectedDate, setSelectedDate] = useState<string>("");
|
||||
const [dateValid, setDateValid] = useState<boolean>(true);
|
||||
const [versionID, setVersionID] = useState<string>("null");
|
||||
const [displayURL, setDisplayURL] = useState<boolean>(false);
|
||||
const [accessKeys, setAccessKeys] = useState<any[]>([]);
|
||||
const [selectedAccessKey, setSelectedAccessKey] = useState<string>("");
|
||||
const [secretKey, setSecretKey] = useState<string>("");
|
||||
const [authType, setAuthType] = useState<string>("acc-list");
|
||||
const [otherAK, setOtherAK] = useState<string>("");
|
||||
|
||||
const initialDate = new Date();
|
||||
|
||||
@@ -134,20 +152,19 @@ const ShareFile = ({
|
||||
dispatch(setModalErrorSnackMessage(error));
|
||||
});
|
||||
|
||||
setIsLoadingVersion(false);
|
||||
setIsLoadingURL(false);
|
||||
return;
|
||||
}
|
||||
setVersionID("null");
|
||||
setIsLoadingVersion(false);
|
||||
setIsLoadingURL(false);
|
||||
return;
|
||||
}
|
||||
setVersionID(dataObject.version_id || "null");
|
||||
setIsLoadingVersion(false);
|
||||
setIsLoadingURL(false);
|
||||
}, [bucketName, dataObject, distributedSetup, dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
if (dateValid && !isLoadingVersion) {
|
||||
setIsLoadingFile(true);
|
||||
if (dateValid && isLoadingURL) {
|
||||
setShareURL("");
|
||||
|
||||
const slDate = new Date(`${selectedDate}`);
|
||||
@@ -157,28 +174,33 @@ const ShareFile = ({
|
||||
(slDate.getTime() - currDate.getTime()) / 1000
|
||||
);
|
||||
|
||||
const accKey = authType === "acc-list" ? selectedAccessKey : otherAK;
|
||||
|
||||
if (diffDate > 0) {
|
||||
api
|
||||
.invoke(
|
||||
"GET",
|
||||
`/api/v1/buckets/${bucketName}/objects/share?prefix=${encodeURLString(
|
||||
dataObject.name
|
||||
)}&version_id=${versionID}${
|
||||
selectedDate !== "" ? `&expires=${diffDate}s` : ""
|
||||
}`
|
||||
)
|
||||
.invoke("POST", `/api/v1/buckets/${bucketName}/objects/share`, {
|
||||
prefix: encodeURLString(dataObject.name),
|
||||
version_id: versionID,
|
||||
expires: selectedDate !== "" ? `${diffDate}s` : "",
|
||||
access_key: accKey,
|
||||
secret_key: secretKey,
|
||||
})
|
||||
.then((res: string) => {
|
||||
setShareURL(res);
|
||||
setIsLoadingFile(false);
|
||||
setIsLoadingURL(false);
|
||||
setDisplayURL(true);
|
||||
})
|
||||
.catch((error: ErrorResponseHandler) => {
|
||||
dispatch(setModalErrorSnackMessage(error));
|
||||
setShareURL("");
|
||||
setIsLoadingFile(false);
|
||||
setIsLoadingURL(false);
|
||||
setDisplayURL(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [
|
||||
secretKey,
|
||||
selectedAccessKey,
|
||||
dataObject,
|
||||
selectedDate,
|
||||
bucketName,
|
||||
@@ -186,80 +208,205 @@ const ShareFile = ({
|
||||
setShareURL,
|
||||
dispatch,
|
||||
distributedSetup,
|
||||
isLoadingVersion,
|
||||
versionID,
|
||||
isLoadingURL,
|
||||
authType,
|
||||
otherAK,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isLoadingAccessKeys) {
|
||||
const userLoggedIn = localStorage.getItem("userLoggedIn");
|
||||
|
||||
api
|
||||
.invoke(
|
||||
"GET",
|
||||
`/api/v1/user/${encodeURLString(userLoggedIn)}/service-accounts`
|
||||
)
|
||||
.then((res: string[]) => {
|
||||
if (res.length === 0) {
|
||||
setAuthType("acc-other");
|
||||
}
|
||||
|
||||
const serviceAccounts = res
|
||||
.sort(stringSort)
|
||||
.map((element) => ({ value: element, label: element }));
|
||||
|
||||
setLoadingAccessKeys(false);
|
||||
setAccessKeys(serviceAccounts);
|
||||
setSelectedAccessKey(serviceAccounts[0].value);
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
dispatch(setErrorSnackMessage(err));
|
||||
setLoadingAccessKeys(false);
|
||||
});
|
||||
}
|
||||
}, [isLoadingAccessKeys, dispatch]);
|
||||
|
||||
const generateLink = () => {
|
||||
setIsLoadingURL(true);
|
||||
};
|
||||
|
||||
const generateAnotherLink = () => {
|
||||
setIsLoadingURL(false);
|
||||
setDisplayURL(false);
|
||||
setSelectedDate("");
|
||||
setShareURL("");
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<ModalWrapper
|
||||
title="Share File"
|
||||
titleIcon={<ShareIcon style={{ fill: "#4CCB92" }} />}
|
||||
modalOpen={open}
|
||||
onClose={() => {
|
||||
closeModalAndRefresh();
|
||||
}}
|
||||
>
|
||||
{isLoadingVersion && (
|
||||
<Grid item xs={12}>
|
||||
<LinearProgress />
|
||||
</Grid>
|
||||
)}
|
||||
{!isLoadingVersion && (
|
||||
<Fragment>
|
||||
<Grid item xs={12} className={classes.shareLinkInfo}>
|
||||
This is a temporary URL with integrated access credentials for
|
||||
sharing objects valid for up to 7 days.
|
||||
<br />
|
||||
<br />
|
||||
The temporary URL expires after the configured time limit.
|
||||
</Grid>
|
||||
<ModalWrapper
|
||||
title="Share File"
|
||||
titleIcon={<ShareIcon style={{ fill: "#4CCB92" }} />}
|
||||
modalOpen={open}
|
||||
onClose={() => {
|
||||
closeModalAndRefresh();
|
||||
}}
|
||||
>
|
||||
{displayURL ? (
|
||||
<Fragment>
|
||||
<Grid item xs={12} className={classes.shareLinkInfo}>
|
||||
This is a temporary URL with integrated access credentials for
|
||||
sharing <strong>{dataObject.name}</strong> until{" "}
|
||||
<strong>
|
||||
{DateTime.fromISO(selectedDate).toFormat(
|
||||
"ccc, LLL dd yyyy HH:mm (ZZZZ)"
|
||||
)}
|
||||
</strong>
|
||||
<br />
|
||||
<Grid item xs={12} className={classes.dateContainer}>
|
||||
<DaysSelector
|
||||
initialDate={initialDate}
|
||||
id="date"
|
||||
label="Active for"
|
||||
maxDays={7}
|
||||
onChange={dateChanged}
|
||||
entity="Link"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
className={`${classes.copyShareLink} ${classes.formFieldRow} `}
|
||||
<br />
|
||||
This temporary URL will expiry after this time.
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
className={`${classes.copyShareLink} ${classes.formFieldRow} `}
|
||||
sx={{ marginTop: 12 }}
|
||||
>
|
||||
<ReadBox
|
||||
actionButton={
|
||||
<CopyToClipboard text={shareURL}>
|
||||
<Button
|
||||
id={"copy-path"}
|
||||
variant="regular"
|
||||
onClick={() => {
|
||||
dispatch(
|
||||
setModalSnackMessage("Share URL Copied to clipboard")
|
||||
);
|
||||
}}
|
||||
disabled={shareURL === "" || isLoadingURL}
|
||||
style={{
|
||||
marginRight: "5px",
|
||||
width: "28px",
|
||||
height: "28px",
|
||||
padding: "0px",
|
||||
}}
|
||||
icon={<CopyIcon />}
|
||||
/>
|
||||
</CopyToClipboard>
|
||||
}
|
||||
>
|
||||
<ReadBox
|
||||
actionButton={
|
||||
<CopyToClipboard text={shareURL}>
|
||||
<Button
|
||||
id={"copy-path"}
|
||||
variant="regular"
|
||||
onClick={() => {
|
||||
dispatch(
|
||||
setModalSnackMessage("Share URL Copied to clipboard")
|
||||
);
|
||||
}}
|
||||
disabled={shareURL === "" || isLoadingFile}
|
||||
style={{
|
||||
marginRight: "5px",
|
||||
width: "28px",
|
||||
height: "28px",
|
||||
padding: "0px",
|
||||
}}
|
||||
icon={<CopyIcon />}
|
||||
/>
|
||||
</CopyToClipboard>
|
||||
}
|
||||
>
|
||||
{shareURL}
|
||||
</ReadBox>
|
||||
{shareURL}
|
||||
</ReadBox>
|
||||
</Grid>
|
||||
<Grid sx={{ display: "flex", justifyContent: "flex-end" }}>
|
||||
<Button
|
||||
id={"generate-link"}
|
||||
variant={"callAction"}
|
||||
type={"button"}
|
||||
onClick={generateAnotherLink}
|
||||
>
|
||||
Generate Another Link
|
||||
</Button>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
) : (
|
||||
<Fragment>
|
||||
<Grid item xs={12} className={classes.shareLinkInfo}>
|
||||
To generate a temporary URL, please provide a set of credentials,
|
||||
this link can ve valid up to 7 days.
|
||||
<br />
|
||||
<br />
|
||||
</Grid>
|
||||
<FormLayout sx={{ border: 0, padding: 0 }}>
|
||||
{accessKeys.length > 0 && (
|
||||
<RadioGroup
|
||||
id={"sign-selector-kind"}
|
||||
selectorOptions={[
|
||||
{ label: "Account's Access Key", value: "acc-list" },
|
||||
{
|
||||
label: "Custom Access Key",
|
||||
value: "acc-other",
|
||||
},
|
||||
]}
|
||||
label={""}
|
||||
name={"sign-selector-kind"}
|
||||
onChange={(e) => {
|
||||
setAuthType(e.target.value);
|
||||
}}
|
||||
currentValue={authType}
|
||||
/>
|
||||
)}
|
||||
{authType === "acc-other" ? (
|
||||
<InputBox
|
||||
id={"other-ak"}
|
||||
value={otherAK}
|
||||
onChange={(e) => {
|
||||
setOtherAK(e.target.value);
|
||||
}}
|
||||
label={"Access Key"}
|
||||
/>
|
||||
) : (
|
||||
<Select
|
||||
options={accessKeys}
|
||||
value={selectedAccessKey}
|
||||
id={"select-ak"}
|
||||
onChange={(item) => {
|
||||
setSelectedAccessKey(item);
|
||||
}}
|
||||
label={"Access Key"}
|
||||
/>
|
||||
)}
|
||||
<InputBox
|
||||
id={"secret-key"}
|
||||
type={"password"}
|
||||
value={secretKey}
|
||||
onChange={(e) => {
|
||||
setSecretKey(e.target.value);
|
||||
}}
|
||||
label={"Secret Key"}
|
||||
/>
|
||||
</FormLayout>
|
||||
|
||||
<Grid item xs={12} className={classes.dateContainer}>
|
||||
<DaysSelector
|
||||
initialDate={initialDate}
|
||||
id="date"
|
||||
label="Link Duration"
|
||||
maxDays={7}
|
||||
onChange={dateChanged}
|
||||
entity="Link"
|
||||
/>
|
||||
</Grid>
|
||||
{isLoadingURL && (
|
||||
<Grid item xs={12}>
|
||||
<LinearProgress />
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)}
|
||||
</ModalWrapper>
|
||||
</React.Fragment>
|
||||
)}
|
||||
<Grid sx={{ display: "flex", justifyContent: "flex-end" }}>
|
||||
<Button
|
||||
id={"generate-link"}
|
||||
variant={"callAction"}
|
||||
type={"button"}
|
||||
onClick={generateLink}
|
||||
disabled={secretKey === "" || !dateValid}
|
||||
>
|
||||
Generate Link
|
||||
</Button>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)}
|
||||
</ModalWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
import { fieldBasic, tooltipHelper } from "../common/styleLibrary";
|
||||
import { LinkIcon, InputLabel, InputBox, Grid } from "mds";
|
||||
import { Grid, InputBox, InputLabel, LinkIcon } from "mds";
|
||||
|
||||
interface IDaysSelector {
|
||||
classes: any;
|
||||
@@ -177,6 +177,10 @@ const DaysSelector = ({
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if (selectedDays <= 0 && selectedHours <= 0 && selectedMinutes <= 0) {
|
||||
valid = false;
|
||||
}
|
||||
|
||||
setValidDate(valid);
|
||||
}, [
|
||||
dateSelected,
|
||||
@@ -203,9 +207,7 @@ const DaysSelector = ({
|
||||
<Fragment>
|
||||
<Grid container className={classes.fieldContainer}>
|
||||
<Grid item xs={12} className={classes.labelContainer}>
|
||||
<InputLabel htmlFor={id} sx={{ marginLeft: "10px" }}>
|
||||
{label}
|
||||
</InputLabel>
|
||||
<InputLabel htmlFor={id}>{label}</InputLabel>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.durationInputs}>
|
||||
<Grid item xs className={classes.dateInputContainer}>
|
||||
|
||||
@@ -21,7 +21,10 @@ import { AppState, useAppDispatch } from "../../../../store";
|
||||
import { Box } from "@mui/material";
|
||||
import { AlertCloseIcon } from "mds";
|
||||
import { Portal } from "@mui/base";
|
||||
import { setErrorSnackMessage } from "../../../../systemSlice";
|
||||
import {
|
||||
setErrorSnackMessage,
|
||||
setModalSnackMessage,
|
||||
} from "../../../../systemSlice";
|
||||
|
||||
interface IMainErrorProps {
|
||||
isModal?: boolean;
|
||||
@@ -51,6 +54,7 @@ const MainError = ({ isModal = false }: IMainErrorProps) => {
|
||||
useEffect(() => {
|
||||
if (!displayErrorMsg) {
|
||||
dispatch(setErrorSnackMessage({ detailedError: "", errorMessage: "" }));
|
||||
dispatch(setModalSnackMessage(""));
|
||||
clearInterval(timerI);
|
||||
}
|
||||
}, [dispatch, displayErrorMsg]);
|
||||
|
||||
@@ -1879,13 +1879,13 @@
|
||||
"@nodelib/fs.scandir" "2.1.5"
|
||||
fastq "^1.6.0"
|
||||
|
||||
"@playwright/test@^1.32.3":
|
||||
version "1.32.3"
|
||||
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.32.3.tgz#75be8346d4ef289896835e1d2a86fdbe3d9be92a"
|
||||
integrity sha512-BvWNvK0RfBriindxhLVabi8BRe3X0J9EVjKlcmhxjg4giWBD/xleLcg2dz7Tx0agu28rczjNIPQWznwzDwVsZQ==
|
||||
"@playwright/test@^1.34.0":
|
||||
version "1.34.0"
|
||||
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.34.0.tgz#1f3359523c3fd7460c83fe83c8152751a9e21f49"
|
||||
integrity sha512-GIALJVODOIrMflLV54H3Cow635OfrTwOu24ZTDyKC66uchtFX2NcCRq83cLdakMjZKYK78lODNLQSYBj2OgaTw==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
playwright-core "1.32.3"
|
||||
playwright-core "1.34.0"
|
||||
optionalDependencies:
|
||||
fsevents "2.3.2"
|
||||
|
||||
@@ -9159,6 +9159,11 @@ playwright-core@1.32.3:
|
||||
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.32.3.tgz#e6dc7db0b49e9b6c0b8073c4a2d789a96f519c48"
|
||||
integrity sha512-SB+cdrnu74ZIn5Ogh/8278ngEh9NEEV0vR4sJFmK04h2iZpybfbqBY0bX6+BLYWVdV12JLLI+JEFtSnYgR+mWg==
|
||||
|
||||
playwright-core@1.34.0:
|
||||
version "1.34.0"
|
||||
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.34.0.tgz#6a8f05c657400677591ed82b6749ef7e120a152d"
|
||||
integrity sha512-fMUY1+iR6kYbJF/EsOOqzBA99ZHXbw9sYPNjwA4X/oV0hVF/1aGlWYBGPVUEqxBkGANDKMziYoOdKGU5DIP5Gg==
|
||||
|
||||
playwright@^1.31.3:
|
||||
version "1.32.3"
|
||||
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.32.3.tgz#88583167880e42ca04fa8c4cc303680f4ff457d0"
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -221,6 +222,10 @@ func (c minioClient) copyObject(ctx context.Context, dst minio.CopyDestOptions,
|
||||
return c.client.CopyObject(ctx, dst, src)
|
||||
}
|
||||
|
||||
func (c minioClient) presignedGetObject(ctx context.Context, bucketName, objectName string, expiry time.Duration, reqParams url.Values) (*url.URL, error) {
|
||||
return c.client.PresignedGetObject(ctx, bucketName, objectName, expiry, reqParams)
|
||||
}
|
||||
|
||||
// MCClient interface with all functions to be implemented
|
||||
// by mock when testing, it should include all mc/S3Client respective api calls
|
||||
// that are used within this project.
|
||||
|
||||
@@ -1858,7 +1858,7 @@ func init() {
|
||||
}
|
||||
},
|
||||
"/buckets/{bucket_name}/objects/share": {
|
||||
"get": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Object"
|
||||
],
|
||||
@@ -1872,21 +1872,12 @@ func init() {
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "prefix",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "version_id",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "expires",
|
||||
"in": "query"
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/shareRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -8319,6 +8310,32 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"shareRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"prefix",
|
||||
"version_id",
|
||||
"access_key",
|
||||
"secret_key"
|
||||
],
|
||||
"properties": {
|
||||
"access_key": {
|
||||
"type": "string"
|
||||
},
|
||||
"expires": {
|
||||
"type": "string"
|
||||
},
|
||||
"prefix": {
|
||||
"type": "string"
|
||||
},
|
||||
"secret_key": {
|
||||
"type": "string"
|
||||
},
|
||||
"version_id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"siteReplicationAddRequest": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -10860,7 +10877,7 @@ func init() {
|
||||
}
|
||||
},
|
||||
"/buckets/{bucket_name}/objects/share": {
|
||||
"get": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Object"
|
||||
],
|
||||
@@ -10874,21 +10891,12 @@ func init() {
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "prefix",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "version_id",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "expires",
|
||||
"in": "query"
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/shareRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -17450,6 +17458,32 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"shareRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"prefix",
|
||||
"version_id",
|
||||
"access_key",
|
||||
"secret_key"
|
||||
],
|
||||
"properties": {
|
||||
"access_key": {
|
||||
"type": "string"
|
||||
},
|
||||
"expires": {
|
||||
"type": "string"
|
||||
},
|
||||
"prefix": {
|
||||
"type": "string"
|
||||
},
|
||||
"secret_key": {
|
||||
"type": "string"
|
||||
},
|
||||
"version_id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"siteReplicationAddRequest": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
||||
@@ -2149,10 +2149,10 @@ func (o *ConsoleAPI) initHandlerCache() {
|
||||
o.handlers["PUT"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["PUT"]["/service-accounts/{access_key}/policy"] = service_account.NewSetServiceAccountPolicy(o.context, o.ServiceAccountSetServiceAccountPolicyHandler)
|
||||
if o.handlers["GET"] == nil {
|
||||
o.handlers["GET"] = make(map[string]http.Handler)
|
||||
if o.handlers["POST"] == nil {
|
||||
o.handlers["POST"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["GET"]["/buckets/{bucket_name}/objects/share"] = object.NewShareObject(o.context, o.ObjectShareObjectHandler)
|
||||
o.handlers["POST"]["/buckets/{bucket_name}/objects/share"] = object.NewShareObject(o.context, o.ObjectShareObjectHandler)
|
||||
if o.handlers["PUT"] == nil {
|
||||
o.handlers["PUT"] = make(map[string]http.Handler)
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ func NewShareObject(ctx *middleware.Context, handler ShareObjectHandler) *ShareO
|
||||
}
|
||||
|
||||
/*
|
||||
ShareObject swagger:route GET /buckets/{bucket_name}/objects/share Object shareObject
|
||||
ShareObject swagger:route POST /buckets/{bucket_name}/objects/share Object shareObject
|
||||
|
||||
Shares an Object on a url
|
||||
*/
|
||||
|
||||
@@ -23,6 +23,7 @@ package object
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
@@ -30,6 +31,8 @@ import (
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/validate"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// NewShareObjectParams creates a new ShareObjectParams object
|
||||
@@ -49,25 +52,16 @@ type ShareObjectParams struct {
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
|
||||
/*
|
||||
Required: true
|
||||
In: body
|
||||
*/
|
||||
Body *models.ShareRequest
|
||||
/*
|
||||
Required: true
|
||||
In: path
|
||||
*/
|
||||
BucketName string
|
||||
/*
|
||||
In: query
|
||||
*/
|
||||
Expires *string
|
||||
/*
|
||||
Required: true
|
||||
In: query
|
||||
*/
|
||||
Prefix string
|
||||
/*
|
||||
Required: true
|
||||
In: query
|
||||
*/
|
||||
VersionID string
|
||||
}
|
||||
|
||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||
@@ -79,27 +73,38 @@ func (o *ShareObjectParams) BindRequest(r *http.Request, route *middleware.Match
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
qs := runtime.Values(r.URL.Query())
|
||||
if runtime.HasBody(r) {
|
||||
defer r.Body.Close()
|
||||
var body models.ShareRequest
|
||||
if err := route.Consumer.Consume(r.Body, &body); err != nil {
|
||||
if err == io.EOF {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
} else {
|
||||
res = append(res, errors.NewParseError("body", "body", "", err))
|
||||
}
|
||||
} else {
|
||||
// validate body object
|
||||
if err := body.Validate(route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
ctx := validate.WithOperationRequest(r.Context())
|
||||
if err := body.ContextValidate(ctx, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
o.Body = &body
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
}
|
||||
|
||||
rBucketName, rhkBucketName, _ := route.Params.GetOK("bucket_name")
|
||||
if err := o.bindBucketName(rBucketName, rhkBucketName, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
qExpires, qhkExpires, _ := qs.GetOK("expires")
|
||||
if err := o.bindExpires(qExpires, qhkExpires, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
qPrefix, qhkPrefix, _ := qs.GetOK("prefix")
|
||||
if err := o.bindPrefix(qPrefix, qhkPrefix, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
qVersionID, qhkVersionID, _ := qs.GetOK("version_id")
|
||||
if err := o.bindVersionID(qVersionID, qhkVersionID, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
@@ -119,63 +124,3 @@ func (o *ShareObjectParams) bindBucketName(rawData []string, hasKey bool, format
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindExpires binds and validates parameter Expires from query.
|
||||
func (o *ShareObjectParams) bindExpires(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: false
|
||||
// AllowEmptyValue: false
|
||||
|
||||
if raw == "" { // empty values pass all other validations
|
||||
return nil
|
||||
}
|
||||
o.Expires = &raw
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindPrefix binds and validates parameter Prefix from query.
|
||||
func (o *ShareObjectParams) bindPrefix(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
if !hasKey {
|
||||
return errors.Required("prefix", "query", rawData)
|
||||
}
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: true
|
||||
// AllowEmptyValue: false
|
||||
|
||||
if err := validate.RequiredString("prefix", "query", raw); err != nil {
|
||||
return err
|
||||
}
|
||||
o.Prefix = raw
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindVersionID binds and validates parameter VersionID from query.
|
||||
func (o *ShareObjectParams) bindVersionID(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
if !hasKey {
|
||||
return errors.Required("version_id", "query", rawData)
|
||||
}
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: true
|
||||
// AllowEmptyValue: false
|
||||
|
||||
if err := validate.RequiredString("version_id", "query", raw); err != nil {
|
||||
return err
|
||||
}
|
||||
o.VersionID = raw
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -33,10 +33,6 @@ import (
|
||||
type ShareObjectURL struct {
|
||||
BucketName string
|
||||
|
||||
Expires *string
|
||||
Prefix string
|
||||
VersionID string
|
||||
|
||||
_basePath string
|
||||
// avoid unkeyed usage
|
||||
_ struct{}
|
||||
@@ -76,28 +72,6 @@ func (o *ShareObjectURL) Build() (*url.URL, error) {
|
||||
}
|
||||
_result.Path = golangswaggerpaths.Join(_basePath, _path)
|
||||
|
||||
qs := make(url.Values)
|
||||
|
||||
var expiresQ string
|
||||
if o.Expires != nil {
|
||||
expiresQ = *o.Expires
|
||||
}
|
||||
if expiresQ != "" {
|
||||
qs.Set("expires", expiresQ)
|
||||
}
|
||||
|
||||
prefixQ := o.Prefix
|
||||
if prefixQ != "" {
|
||||
qs.Set("prefix", prefixQ)
|
||||
}
|
||||
|
||||
versionIDQ := o.VersionID
|
||||
if versionIDQ != "" {
|
||||
qs.Set("version_id", versionIDQ)
|
||||
}
|
||||
|
||||
_result.RawQuery = qs.Encode()
|
||||
|
||||
return &_result, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/klauspost/compress/zip"
|
||||
@@ -117,7 +119,7 @@ func registerObjectsHandlers(api *operations.ConsoleAPI) {
|
||||
})
|
||||
// get share object url
|
||||
api.ObjectShareObjectHandler = objectApi.ShareObjectHandlerFunc(func(params objectApi.ShareObjectParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := getShareObjectResponse(session, params)
|
||||
resp, err := getShareObjectResponse(params)
|
||||
if err != nil {
|
||||
return objectApi.NewShareObjectDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -892,34 +894,60 @@ func uploadFiles(ctx context.Context, client MinioClient, params objectApi.PostB
|
||||
return nil
|
||||
}
|
||||
|
||||
// getShareObjectResponse returns a share object url
|
||||
func getShareObjectResponse(session *models.Principal, params objectApi.ShareObjectParams) (*string, *models.Error) {
|
||||
// getShareObjectResponse returns a share object url, Session is omitted as we will sign the URl with a new static token
|
||||
func getShareObjectResponse(params objectApi.ShareObjectParams) (*string, *models.Error) {
|
||||
ctx := params.HTTPRequest.Context()
|
||||
bodyPrefix := *params.Body.Prefix
|
||||
|
||||
accessKey := *params.Body.AccessKey
|
||||
secretKey := *params.Body.SecretKey
|
||||
|
||||
creds := credentials.NewStaticV4(accessKey, secretKey, "")
|
||||
|
||||
mClient, err := minio.New(getMinIOEndpoint(), &minio.Options{
|
||||
Creds: creds,
|
||||
Secure: getMinIOEndpointIsSecure(),
|
||||
Transport: GetConsoleHTTPClient(getMinIOServer()).Transport,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
minioClient := minioClient{client: mClient}
|
||||
|
||||
var prefix string
|
||||
if params.Prefix != "" {
|
||||
encodedPrefix := SanitizeEncodedPrefix(params.Prefix)
|
||||
if bodyPrefix != "" {
|
||||
encodedPrefix := SanitizeEncodedPrefix(bodyPrefix)
|
||||
decodedPrefix, err := base64.StdEncoding.DecodeString(encodedPrefix)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
prefix = string(decodedPrefix)
|
||||
}
|
||||
s3Client, err := newS3BucketClient(session, params.BucketName, prefix)
|
||||
|
||||
expireDuration := time.Duration(604800) * time.Second
|
||||
|
||||
if params.Body.Expires != "" {
|
||||
expireDuration, err = time.ParseDuration(params.Body.Expires)
|
||||
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
reqParams := make(url.Values)
|
||||
if *params.Body.VersionID != "" {
|
||||
reqParams.Set("versionId", *params.Body.VersionID)
|
||||
}
|
||||
|
||||
urlParams, err := minioClient.presignedGetObject(ctx, params.BucketName, prefix, expireDuration, reqParams)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a mc S3Client interface implementation
|
||||
// defining the client to be used
|
||||
mcClient := mcClient{client: s3Client}
|
||||
var expireDuration string
|
||||
if params.Expires != nil {
|
||||
expireDuration = *params.Expires
|
||||
}
|
||||
url, err := getShareObjectURL(ctx, mcClient, params.VersionID, expireDuration)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return url, nil
|
||||
|
||||
stringURL := urlParams.String()
|
||||
|
||||
return &stringURL, nil
|
||||
}
|
||||
|
||||
func getShareObjectURL(ctx context.Context, client MCClient, versionID string, duration string) (url *string, err error) {
|
||||
|
||||
38
swagger.yml
38
swagger.yml
@@ -484,7 +484,7 @@ paths:
|
||||
- Object
|
||||
|
||||
/buckets/{bucket_name}/objects/share:
|
||||
get:
|
||||
post:
|
||||
summary: Shares an Object on a url
|
||||
operationId: ShareObject
|
||||
parameters:
|
||||
@@ -492,18 +492,11 @@ paths:
|
||||
in: path
|
||||
required: true
|
||||
type: string
|
||||
- name: prefix
|
||||
in: query
|
||||
- name: body
|
||||
in: body
|
||||
required: true
|
||||
type: string
|
||||
- name: version_id
|
||||
in: query
|
||||
required: true
|
||||
type: string
|
||||
- name: expires
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
schema:
|
||||
$ref: "#/definitions/shareRequest"
|
||||
responses:
|
||||
200:
|
||||
description: A successful response.
|
||||
@@ -6152,4 +6145,23 @@ definitions:
|
||||
groups:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
type: string
|
||||
|
||||
shareRequest:
|
||||
type: object
|
||||
required:
|
||||
- prefix
|
||||
- version_id
|
||||
- access_key
|
||||
- secret_key
|
||||
properties:
|
||||
prefix:
|
||||
type: string
|
||||
version_id:
|
||||
type: string
|
||||
expires:
|
||||
type: string
|
||||
access_key:
|
||||
type: string
|
||||
secret_key:
|
||||
type: string
|
||||
Reference in New Issue
Block a user