diff --git a/integration/service_account_test.go b/integration/service_account_test.go
index 43e8bf861..70d7fcc9d 100644
--- a/integration/service_account_test.go
+++ b/integration/service_account_test.go
@@ -28,7 +28,6 @@ import (
"github.com/go-openapi/swag"
- iampolicy "github.com/minio/pkg/v2/policy"
"github.com/stretchr/testify/assert"
)
@@ -52,6 +51,21 @@ func TestAddServiceAccount(t *testing.T) {
requestDataAddServiceAccount := map[string]interface{}{
"accessKey": "testuser1",
"secretKey": "password",
+ "policy": `{
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Action": [
+ "s3:GetBucketLocation",
+ "s3:GetObject"
+ ],
+ "Resource": [
+ "arn:aws:s3:::*"
+ ]
+ }
+ ]
+}`,
}
requestDataJSON, _ := json.Marshal(requestDataAddServiceAccount)
@@ -75,75 +89,6 @@ func TestAddServiceAccount(t *testing.T) {
assert.Equal(201, response.StatusCode, "Status Code is incorrect")
}
- requestDataPolicy := map[string]interface{}{
- "policy": `
- {
- "Version": "2012-10-17",
- "Statement": [
- {
- "Effect": "Allow",
- "Action": [
- "s3:GetBucketLocation",
- "s3:GetObject"
- ],
- "Resource": [
- "arn:aws:s3:::*"
- ]
- }
- ]
-}`,
- }
- requestDataJSON, _ = json.Marshal(requestDataPolicy)
- requestDataBody = bytes.NewReader(requestDataJSON)
- request, err = http.NewRequest(
- "PUT", "http://localhost:9090/api/v1/service-accounts/"+base64.StdEncoding.EncodeToString([]byte("testuser1"))+"/policy", requestDataBody)
- if err != nil {
- log.Println(err)
- return
- }
- request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
- request.Header.Add("Content-Type", "application/json")
- response, err = client.Do(request)
- if err != nil {
- log.Println(err)
- return
- }
- if response != nil {
- fmt.Println("POST StatusCode:", response.StatusCode)
- assert.Equal(200, response.StatusCode, "Status Code is incorrect")
- }
-
- // Test policy
- request, err = http.NewRequest(
- "GET", "http://localhost:9090/api/v1/service-accounts/"+base64.StdEncoding.EncodeToString([]byte("testuser1"))+"/policy", nil)
- if err != nil {
- log.Println(err)
- return
- }
- request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
- request.Header.Add("Content-Type", "application/json")
- response, err = client.Do(request)
- if err != nil {
- log.Println(err)
- return
- }
- if response != nil {
- fmt.Println("POST StatusCode:", response.StatusCode)
- assert.Equal(200, response.StatusCode, "Status Code is incorrect")
- buf := new(bytes.Buffer)
- buf.ReadFrom(response.Body)
- var actual *iampolicy.Policy
- var expected *iampolicy.Policy
- json.Unmarshal(buf.Bytes(), actual)
- policy, err := json.Marshal(requestDataAddServiceAccount["policy"])
- if err != nil {
- log.Println(err)
- return
- }
- json.Unmarshal(policy, expected)
- assert.Equal(expected, actual)
- }
-
// {{baseUrl}}/user?name=proident velit
// Investiga como se borra en el browser.
request, err = http.NewRequest(
diff --git a/models/service_account.go b/models/service_account.go
new file mode 100644
index 000000000..91f909f9a
--- /dev/null
+++ b/models/service_account.go
@@ -0,0 +1,85 @@
+// 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/strfmt"
+ "github.com/go-openapi/swag"
+)
+
+// ServiceAccount service account
+//
+// swagger:model serviceAccount
+type ServiceAccount struct {
+
+ // account status
+ AccountStatus string `json:"accountStatus,omitempty"`
+
+ // description
+ Description string `json:"description,omitempty"`
+
+ // expiration
+ Expiration string `json:"expiration,omitempty"`
+
+ // implied policy
+ ImpliedPolicy bool `json:"impliedPolicy,omitempty"`
+
+ // name
+ Name string `json:"name,omitempty"`
+
+ // parent user
+ ParentUser string `json:"parentUser,omitempty"`
+
+ // policy
+ Policy string `json:"policy,omitempty"`
+}
+
+// Validate validates this service account
+func (m *ServiceAccount) Validate(formats strfmt.Registry) error {
+ return nil
+}
+
+// ContextValidate validates this service account based on context it is used
+func (m *ServiceAccount) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
+ return nil
+}
+
+// MarshalBinary interface implementation
+func (m *ServiceAccount) MarshalBinary() ([]byte, error) {
+ if m == nil {
+ return nil, nil
+ }
+ return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *ServiceAccount) UnmarshalBinary(b []byte) error {
+ var res ServiceAccount
+ if err := swag.ReadJSON(b, &res); err != nil {
+ return err
+ }
+ *m = res
+ return nil
+}
diff --git a/models/service_account_request.go b/models/service_account_request.go
index f23cd13dd..ea11c5c94 100644
--- a/models/service_account_request.go
+++ b/models/service_account_request.go
@@ -34,6 +34,18 @@ import (
// swagger:model serviceAccountRequest
type ServiceAccountRequest struct {
+ // comment
+ Comment string `json:"comment,omitempty"`
+
+ // description
+ Description string `json:"description,omitempty"`
+
+ // expiry
+ Expiry string `json:"expiry,omitempty"`
+
+ // name
+ Name string `json:"name,omitempty"`
+
// policy to be applied to the Service Account if any
Policy string `json:"policy,omitempty"`
}
diff --git a/models/service_account_request_creds.go b/models/service_account_request_creds.go
index 276ccdc03..fed5a89f1 100644
--- a/models/service_account_request_creds.go
+++ b/models/service_account_request_creds.go
@@ -37,6 +37,18 @@ type ServiceAccountRequestCreds struct {
// access key
AccessKey string `json:"accessKey,omitempty"`
+ // comment
+ Comment string `json:"comment,omitempty"`
+
+ // description
+ Description string `json:"description,omitempty"`
+
+ // expiry
+ Expiry string `json:"expiry,omitempty"`
+
+ // name
+ Name string `json:"name,omitempty"`
+
// policy to be applied to the Service Account if any
Policy string `json:"policy,omitempty"`
diff --git a/models/add_service_account_policy_request.go b/models/update_service_account_request.go
similarity index 62%
rename from models/add_service_account_policy_request.go
rename to models/update_service_account_request.go
index 149824aab..1e4dfa8d0 100644
--- a/models/add_service_account_policy_request.go
+++ b/models/update_service_account_request.go
@@ -31,18 +31,33 @@ import (
"github.com/go-openapi/validate"
)
-// AddServiceAccountPolicyRequest add service account policy request
+// UpdateServiceAccountRequest update service account request
//
-// swagger:model addServiceAccountPolicyRequest
-type AddServiceAccountPolicyRequest struct {
+// swagger:model updateServiceAccountRequest
+type UpdateServiceAccountRequest struct {
+
+ // description
+ Description string `json:"description,omitempty"`
+
+ // expiry
+ Expiry string `json:"expiry,omitempty"`
+
+ // name
+ Name string `json:"name,omitempty"`
// policy
// Required: true
Policy *string `json:"policy"`
+
+ // secret key
+ SecretKey string `json:"secretKey,omitempty"`
+
+ // status
+ Status string `json:"status,omitempty"`
}
-// Validate validates this add service account policy request
-func (m *AddServiceAccountPolicyRequest) Validate(formats strfmt.Registry) error {
+// Validate validates this update service account request
+func (m *UpdateServiceAccountRequest) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validatePolicy(formats); err != nil {
@@ -55,7 +70,7 @@ func (m *AddServiceAccountPolicyRequest) Validate(formats strfmt.Registry) error
return nil
}
-func (m *AddServiceAccountPolicyRequest) validatePolicy(formats strfmt.Registry) error {
+func (m *UpdateServiceAccountRequest) validatePolicy(formats strfmt.Registry) error {
if err := validate.Required("policy", "body", m.Policy); err != nil {
return err
@@ -64,13 +79,13 @@ func (m *AddServiceAccountPolicyRequest) validatePolicy(formats strfmt.Registry)
return nil
}
-// ContextValidate validates this add service account policy request based on context it is used
-func (m *AddServiceAccountPolicyRequest) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
+// ContextValidate validates this update service account request based on context it is used
+func (m *UpdateServiceAccountRequest) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
-func (m *AddServiceAccountPolicyRequest) MarshalBinary() ([]byte, error) {
+func (m *UpdateServiceAccountRequest) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
@@ -78,8 +93,8 @@ func (m *AddServiceAccountPolicyRequest) MarshalBinary() ([]byte, error) {
}
// UnmarshalBinary interface implementation
-func (m *AddServiceAccountPolicyRequest) UnmarshalBinary(b []byte) error {
- var res AddServiceAccountPolicyRequest
+func (m *UpdateServiceAccountRequest) UnmarshalBinary(b []byte) error {
+ var res UpdateServiceAccountRequest
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
diff --git a/portal-ui/src/api/consoleApi.ts b/portal-ui/src/api/consoleApi.ts
index ce0fa3a4e..9937cb28a 100644
--- a/portal-ui/src/api/consoleApi.ts
+++ b/portal-ui/src/api/consoleApi.ts
@@ -214,8 +214,13 @@ export interface AddPolicyRequest {
policy: string;
}
-export interface AddServiceAccountPolicyRequest {
+export interface UpdateServiceAccountRequest {
policy: string;
+ secretKey?: string;
+ name?: string;
+ description?: string;
+ expiry?: string;
+ status?: string;
}
export interface ListPoliciesResponse {
@@ -727,6 +732,16 @@ export interface BulkUserGroups {
groups: string[];
}
+export interface ServiceAccount {
+ parentUser?: string;
+ accountStatus?: string;
+ impliedPolicy?: boolean;
+ policy?: string;
+ name?: string;
+ description?: string;
+ expiration?: string;
+}
+
export type ServiceAccounts = {
accountStatus?: string;
name?: string;
@@ -738,6 +753,10 @@ export type ServiceAccounts = {
export interface ServiceAccountRequest {
/** policy to be applied to the Service Account if any */
policy?: string;
+ name?: string;
+ description?: string;
+ expiry?: string;
+ comment?: string;
}
export interface ServiceAccountRequestCreds {
@@ -745,6 +764,10 @@ export interface ServiceAccountRequestCreds {
policy?: string;
accessKey?: string;
secretKey?: string;
+ name?: string;
+ description?: string;
+ expiry?: string;
+ comment?: string;
}
export interface ServiceAccountCreds {
@@ -1720,9 +1743,10 @@ export class HttpClient {
? { "Content-Type": type }
: {}),
},
- signal: cancelToken
- ? this.createAbortSignal(cancelToken)
- : requestParams.signal,
+ signal:
+ (cancelToken
+ ? this.createAbortSignal(cancelToken)
+ : requestParams.signal) || null,
body:
typeof body === "undefined" || body === null
? null
@@ -3052,23 +3076,6 @@ export class Api<
...params,
}),
- /**
- * No description
- *
- * @tags ServiceAccount
- * @name DeleteServiceAccount
- * @summary Delete Service Account
- * @request DELETE:/service-accounts/{access_key}
- * @secure
- */
- deleteServiceAccount: (accessKey: string, params: RequestParams = {}) =>
- this.request({
- path: `/service-accounts/${accessKey}`,
- method: "DELETE",
- secure: true,
- ...params,
- }),
-
/**
* No description
*
@@ -3094,14 +3101,14 @@ export class Api<
* No description
*
* @tags ServiceAccount
- * @name GetServiceAccountPolicy
- * @summary Get Service Account Policy
- * @request GET:/service-accounts/{access_key}/policy
+ * @name GetServiceAccount
+ * @summary Get Service Account
+ * @request GET:/service-accounts/{access_key}
* @secure
*/
- getServiceAccountPolicy: (accessKey: string, params: RequestParams = {}) =>
- this.request({
- path: `/service-accounts/${accessKey}/policy`,
+ getServiceAccount: (accessKey: string, params: RequestParams = {}) =>
+ this.request({
+ path: `/service-accounts/${accessKey}`,
method: "GET",
secure: true,
format: "json",
@@ -3112,24 +3119,41 @@ export class Api<
* No description
*
* @tags ServiceAccount
- * @name SetServiceAccountPolicy
+ * @name UpdateServiceAccount
* @summary Set Service Account Policy
- * @request PUT:/service-accounts/{access_key}/policy
+ * @request PUT:/service-accounts/{access_key}
* @secure
*/
- setServiceAccountPolicy: (
+ updateServiceAccount: (
accessKey: string,
- policy: AddServiceAccountPolicyRequest,
+ body: UpdateServiceAccountRequest,
params: RequestParams = {},
) =>
this.request({
- path: `/service-accounts/${accessKey}/policy`,
+ path: `/service-accounts/${accessKey}`,
method: "PUT",
- body: policy,
+ body: body,
secure: true,
type: ContentType.Json,
...params,
}),
+
+ /**
+ * No description
+ *
+ * @tags ServiceAccount
+ * @name DeleteServiceAccount
+ * @summary Delete Service Account
+ * @request DELETE:/service-accounts/{access_key}
+ * @secure
+ */
+ deleteServiceAccount: (accessKey: string, params: RequestParams = {}) =>
+ this.request({
+ path: `/service-accounts/${accessKey}`,
+ method: "DELETE",
+ secure: true,
+ ...params,
+ }),
};
serviceAccountCredentials = {
/**
diff --git a/portal-ui/src/screens/Console/Account/Account.tsx b/portal-ui/src/screens/Console/Account/Account.tsx
index 753fa8b6f..60dc30008 100644
--- a/portal-ui/src/screens/Console/Account/Account.tsx
+++ b/portal-ui/src/screens/Console/Account/Account.tsx
@@ -37,7 +37,7 @@ import withSuspense from "../Common/Components/withSuspense";
import { selectSAs } from "../Configurations/utils";
import DeleteMultipleServiceAccounts from "../Users/DeleteMultipleServiceAccounts";
-import ServiceAccountPolicy from "./ServiceAccountPolicy";
+import EditServiceAccount from "./EditServiceAccount";
import { selFeatures } from "../consoleSlice";
import TooltipWrapper from "../Common/TooltipWrapper/TooltipWrapper";
@@ -82,7 +82,7 @@ const Account = () => {
useState(false);
const [selectedSAs, setSelectedSAs] = useState([]);
const [deleteMultipleOpen, setDeleteMultipleOpen] = useState(false);
- const [policyOpen, setPolicyOpen] = useState(false);
+ const [isEditOpen, setIsEditOpen] = useState(false);
const userIDP = (features && features.includes("external-idp")) || false;
@@ -137,13 +137,13 @@ const Account = () => {
}
};
- const policyModalOpen = (selectedServiceAccount: string) => {
+ const editModalOpen = (selectedServiceAccount: string) => {
setSelectedServiceAccount(selectedServiceAccount);
- setPolicyOpen(true);
+ setIsEditOpen(true);
};
const closePolicyModal = () => {
- setPolicyOpen(false);
+ setIsEditOpen(false);
setLoading(true);
};
@@ -157,7 +157,7 @@ const Account = () => {
type: "view",
onClick: (value: any) => {
if (value) {
- policyModalOpen(value.accessKey);
+ editModalOpen(value.accessKey);
}
},
},
@@ -169,6 +169,14 @@ const Account = () => {
}
},
},
+ {
+ type: "edit",
+ onClick: (value: any) => {
+ if (value) {
+ editModalOpen(value.accessKey);
+ }
+ },
+ },
];
const filteredRecords = records.filter(
@@ -195,9 +203,9 @@ const Account = () => {
/>
)}
- {policyOpen && (
-
diff --git a/portal-ui/src/screens/Console/Account/AddServiceAccountScreen.tsx b/portal-ui/src/screens/Console/Account/AddServiceAccountScreen.tsx
index a5fd6f661..7924adca6 100644
--- a/portal-ui/src/screens/Console/Account/AddServiceAccountScreen.tsx
+++ b/portal-ui/src/screens/Console/Account/AddServiceAccountScreen.tsx
@@ -29,6 +29,7 @@ import {
Switch,
ServiceAccountIcon,
HelpTip,
+ DateTimeInput,
} from "mds";
import { modalStyleUtils } from "../Common/FormComponents/common/styleLibrary";
import { NewServiceAccount } from "../Common/CredentialsPrompt/types";
@@ -59,6 +60,11 @@ const AddServiceAccount = () => {
useState(null);
const [policyJSON, setPolicyJSON] = useState("");
+ const [name, setName] = useState("");
+ const [description, setDescription] = useState("");
+ const [comments, setComments] = useState("");
+ const [expiry, setExpiry] = useState();
+
useEffect(() => {
dispatch(setHelpName("add_service_account"));
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -66,12 +72,17 @@ const AddServiceAccount = () => {
useEffect(() => {
if (addSending) {
+ const expiryDt = expiry ? expiry.toJSDate().toISOString() : null;
api.serviceAccountCredentials
.createServiceAccountCreds(
{
policy: policyJSON,
accessKey: accessKey,
secretKey: secretKey,
+ description: description,
+ comment: comments,
+ name: name,
+ expiry: expiryDt,
},
{ type: ContentType.Json },
)
@@ -89,7 +100,18 @@ const AddServiceAccount = () => {
dispatch(setErrorSnackMessage(errorToHandler(res.error)));
});
}
- }, [addSending, setAddSending, dispatch, policyJSON, accessKey, secretKey]);
+ }, [
+ addSending,
+ setAddSending,
+ dispatch,
+ policyJSON,
+ accessKey,
+ secretKey,
+ name,
+ description,
+ expiry,
+ comments,
+ ]);
useEffect(() => {
if (isRestrictedByPolicy) {
@@ -221,6 +243,73 @@ const AddServiceAccount = () => {
)}
+
+
+
+ {
+ setExpiry(e);
+ }}
+ id="expiryTime"
+ label={"Expiry"}
+ timeFormat={"24h"}
+ secondsSelector={false}
+ />
+
+
+ {
+ setName(e.target.value);
+ }}
+ />
+ {
+ setDescription(e.target.value);
+ }}
+ />
+