diff --git a/integration/user_api_bucket_test.go b/integration/user_api_bucket_test.go index 54444849a9..37976eab41 100644 --- a/integration/user_api_bucket_test.go +++ b/integration/user_api_bucket_test.go @@ -461,24 +461,12 @@ func ListObjects(bucketName, prefix, withVersions string) (*http.Response, error return response, err } -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) - +func SharesAnObjectOnAUrl(bucketName, prefix, versionID, expires string) (*http.Response, error) { + // Helper function to share an object on a url request, err := http.NewRequest( - "POST", - "http://localhost:9090/api/v1/buckets/"+bucketName+"/objects/share", - requestDataBody, + "GET", + "http://localhost:9090/api/v1/buckets/"+bucketName+"/objects/share?prefix="+prefix+"&version_id="+versionID+"&expires="+expires, + nil, ) if err != nil { log.Println(err) @@ -755,39 +743,6 @@ 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) @@ -1559,8 +1514,6 @@ 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) { @@ -1581,21 +1534,6 @@ 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 @@ -1623,7 +1561,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", accessKey, secretKey) + shareResponse, shareError := SharesAnObjectOnAUrl(bucketName, tt.args.prefix, versionID, "604800s") assert.Nil(shareError) if shareError != nil { log.Println(shareError) diff --git a/models/share_request.go b/models/share_request.go deleted file mode 100644 index b12a7f041e..0000000000 --- a/models/share_request.go +++ /dev/null @@ -1,142 +0,0 @@ -// 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 . -// - -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 -} diff --git a/portal-ui/src/api/consoleApi.ts b/portal-ui/src/api/consoleApi.ts index cc4896ef0a..390bd9db75 100644 --- a/portal-ui/src/api/consoleApi.ts +++ b/portal-ui/src/api/consoleApi.ts @@ -1494,14 +1494,6 @@ 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; export type ResponseFormat = keyof Omit; @@ -2179,20 +2171,23 @@ export class Api< * @tags Object * @name ShareObject * @summary Shares an Object on a url - * @request POST:/buckets/{bucket_name}/objects/share + * @request GET:/buckets/{bucket_name}/objects/share * @secure */ shareObject: ( bucketName: string, - body: ShareRequest, + query: { + prefix: string; + version_id: string; + expires?: string; + }, params: RequestParams = {} ) => this.request({ path: `/buckets/${bucketName}/objects/share`, - method: "POST", - body: body, + method: "GET", + query: query, secure: true, - type: ContentType.Json, format: "json", ...params, }), diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/ShareFile.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/ShareFile.tsx index 291c284e22..053ff00494 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/ShareFile.tsx +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/ShareFile.tsx @@ -16,26 +16,9 @@ import React, { Fragment, useEffect, useState } from "react"; import { useSelector } from "react-redux"; -import { Theme } from "@mui/material/styles"; -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 { Button, CopyIcon, ReadBox, ShareIcon, Grid } from "mds"; import CopyToClipboard from "react-copy-to-clipboard"; import LinearProgress from "@mui/material/LinearProgress"; -import { - formFieldStyles, - modalStyleUtils, -} from "../../../../Common/FormComponents/common/styleLibrary"; import { IFileInfo } from "./types"; import { ErrorResponseHandler } from "../../../../../../common/types"; @@ -45,42 +28,12 @@ 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({ - shareLinkInfo: { - fontSize: 14, - fontWeight: 400, - }, - copyShareLink: { - display: "flex", - "@media (max-width: 900px)": { - flexFlow: "column", - alignItems: "center", - justifyContent: "center", - }, - }, - copyShareLinkInput: { - "& div:first-child": { - marginTop: 0, - }, - "@media (max-width: 900px)": { - minWidth: 250, - }, - }, - ...modalStyleUtils, - ...formFieldStyles, - }); interface IShareFileProps { - classes: any; open: boolean; bucketName: string; dataObject: IFileInfo; @@ -88,7 +41,6 @@ interface IShareFileProps { } const ShareFile = ({ - classes, open, closeModalAndRefresh, bucketName, @@ -97,17 +49,11 @@ const ShareFile = ({ const dispatch = useAppDispatch(); const distributedSetup = useSelector(selDistSet); const [shareURL, setShareURL] = useState(""); - const [isLoadingURL, setIsLoadingURL] = useState(false); - const [isLoadingAccessKeys, setLoadingAccessKeys] = useState(true); + const [isLoadingVersion, setIsLoadingVersion] = useState(true); + const [isLoadingFile, setIsLoadingFile] = useState(false); const [selectedDate, setSelectedDate] = useState(""); const [dateValid, setDateValid] = useState(true); const [versionID, setVersionID] = useState("null"); - const [displayURL, setDisplayURL] = useState(false); - const [accessKeys, setAccessKeys] = useState([]); - const [selectedAccessKey, setSelectedAccessKey] = useState(""); - const [secretKey, setSecretKey] = useState(""); - const [authType, setAuthType] = useState("acc-list"); - const [otherAK, setOtherAK] = useState(""); const initialDate = new Date(); @@ -152,19 +98,20 @@ const ShareFile = ({ dispatch(setModalErrorSnackMessage(error)); }); - setIsLoadingURL(false); + setIsLoadingVersion(false); return; } setVersionID("null"); - setIsLoadingURL(false); + setIsLoadingVersion(false); return; } setVersionID(dataObject.version_id || "null"); - setIsLoadingURL(false); + setIsLoadingVersion(false); }, [bucketName, dataObject, distributedSetup, dispatch]); useEffect(() => { - if (dateValid && isLoadingURL) { + if (dateValid && !isLoadingVersion) { + setIsLoadingFile(true); setShareURL(""); const slDate = new Date(`${selectedDate}`); @@ -174,33 +121,28 @@ const ShareFile = ({ (slDate.getTime() - currDate.getTime()) / 1000 ); - const accKey = authType === "acc-list" ? selectedAccessKey : otherAK; - if (diffDate > 0) { api - .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, - }) + .invoke( + "GET", + `/api/v1/buckets/${bucketName}/objects/share?prefix=${encodeURLString( + dataObject.name + )}&version_id=${versionID}${ + selectedDate !== "" ? `&expires=${diffDate}s` : "" + }` + ) .then((res: string) => { setShareURL(res); - setIsLoadingURL(false); - setDisplayURL(true); + setIsLoadingFile(false); }) .catch((error: ErrorResponseHandler) => { dispatch(setModalErrorSnackMessage(error)); setShareURL(""); - setIsLoadingURL(false); - setDisplayURL(false); + setIsLoadingFile(false); }); } } }, [ - secretKey, - selectedAccessKey, dataObject, selectedDate, bucketName, @@ -208,206 +150,89 @@ 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 ( - } - modalOpen={open} - onClose={() => { - closeModalAndRefresh(); - }} - > - {displayURL ? ( - - - This is a temporary URL with integrated access credentials for - sharing {dataObject.name} until{" "} - - {DateTime.fromISO(selectedDate).toFormat( - "ccc, LLL dd yyyy HH:mm (ZZZZ)" - )} - -
-
- This temporary URL will expiry after this time. -
- - - - -
- ) : ( - - - To generate a temporary URL, please provide a set of credentials, - this link can be valid up to 7 days. -
+ This is a temporary URL with integrated access credentials for + sharing objects valid for up to 7 days. +
+
+ The temporary URL expires after the configured time limit. +

- - - {accessKeys.length > 0 && ( - { - setAuthType(e.target.value); - }} - currentValue={authType} - /> - )} - {authType === "acc-other" ? ( - { - setOtherAK(e.target.value); - }} - label={"Access Key"} - /> - ) : ( -