Added bulk functionality for add users to groups (#68)

Added functionality in users module to add multiple users to multiple groups at once.

Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
Alex
2020-04-15 20:08:35 -05:00
committed by GitHub
parent 82ea3c1ac4
commit 540ff31784
18 changed files with 1397 additions and 41 deletions

View File

@@ -0,0 +1,98 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 (
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// BulkUserGroups bulk user groups
//
// swagger:model bulkUserGroups
type BulkUserGroups struct {
// groups
// Required: true
Groups []string `json:"groups"`
// users
// Required: true
Users []string `json:"users"`
}
// Validate validates this bulk user groups
func (m *BulkUserGroups) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateGroups(formats); err != nil {
res = append(res, err)
}
if err := m.validateUsers(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *BulkUserGroups) validateGroups(formats strfmt.Registry) error {
if err := validate.Required("groups", "body", m.Groups); err != nil {
return err
}
return nil
}
func (m *BulkUserGroups) validateUsers(formats strfmt.Registry) error {
if err := validate.Required("users", "body", m.Users); err != nil {
return err
}
return nil
}
// MarshalBinary interface implementation
func (m *BulkUserGroups) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *BulkUserGroups) UnmarshalBinary(b []byte) error {
var res BulkUserGroups
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -0,0 +1,160 @@
// This file is part of MinIO Console Server
// Copyright (c) 2019 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/>.
import React, { useState, useEffect } from "react";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { Button, LinearProgress } from "@material-ui/core";
import Grid from "@material-ui/core/Grid";
import Typography from "@material-ui/core/Typography";
import api from "../../../common/api";
import GroupsSelectors from "./GroupsSelectors";
import Title from "../../../common/Title";
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
interface IAddToGroup {
open: boolean;
checkedUsers: any;
closeModalAndRefresh: any;
classes: any;
}
const styles = (theme: Theme) =>
createStyles({
errorBlock: {
color: "red"
},
strongText: {
fontWeight: 700
},
keyName: {
marginLeft: 5
},
buttonContainer: {
textAlign: "right"
}
});
const AddToGroup = ({
open,
checkedUsers,
closeModalAndRefresh,
classes
}: IAddToGroup) => {
//Local States
const [saving, isSaving] = useState<boolean>(false);
const [updatingError, setError] = useState<string>("");
const [selectedGroups, setSelectedGroups] = useState<string[]>([]);
//Effects
useEffect(() => {
if (saving) {
if (selectedGroups.length > 0) {
api
.invoke("PUT", "/api/v1/users-groups-bulk", {
groups: selectedGroups,
users: checkedUsers
})
.then(res => {
isSaving(false);
setError("");
closeModalAndRefresh(true);
})
.catch(err => {
isSaving(false);
setError(err);
});
} else {
isSaving(false);
setError("You need to select at least one group to assign");
}
}
}, [
saving,
isSaving,
setError,
closeModalAndRefresh,
selectedGroups,
checkedUsers
]);
//Fetch Actions
const setSaving = (event: React.FormEvent) => {
event.preventDefault();
isSaving(true);
};
return (
<ModalWrapper
modalOpen={open}
onClose={() => {
closeModalAndRefresh(false);
}}
title="Add Users to Group"
>
<form noValidate autoComplete="off" onSubmit={setSaving}>
<Grid container>
{updatingError !== "" && (
<Grid item xs={12}>
<Typography
component="p"
variant="body1"
className={classes.errorBlock}
>
{updatingError}
</Typography>
</Grid>
)}
<Grid item xs={12}>
<Title>Users to be altered</Title>
</Grid>
<Grid item xs={12}>
{checkedUsers.join(", ")}
</Grid>
<Grid item xs={12}>
<br />
</Grid>
<Grid item xs={12}>
<GroupsSelectors
selectedGroups={selectedGroups}
setSelectedGroups={setSelectedGroups}
/>
</Grid>
<Grid item xs={12}>
<br />
</Grid>
<Grid item xs={12} className={classes.buttonContainer}>
<Button
type="submit"
variant="contained"
color="primary"
disabled={saving}
>
Save
</Button>
</Grid>
{saving && (
<Grid item xs={12}>
<LinearProgress />
</Grid>
)}
</Grid>
</form>
</ModalWrapper>
);
};
export default withStyles(styles)(AddToGroup);

View File

@@ -55,8 +55,6 @@ interface IAddUserContentState {
accessKey: string;
secretKey: string;
selectedGroups: string[];
loadingGroups: boolean;
groupsList: any[];
enabled: string;
}
@@ -69,10 +67,8 @@ class AddUserContent extends React.Component<
addError: "",
accessKey: "",
secretKey: "",
selectedGroups: [],
loadingGroups: false,
groupsList: [],
enabled: "enabled"
enabled: "enabled",
selectedGroups: []
};
componentDidMount(): void {
@@ -188,8 +184,6 @@ class AddUserContent extends React.Component<
accessKey,
secretKey,
selectedGroups,
loadingGroups,
groupsList,
enabled
} = this.state;
@@ -268,8 +262,6 @@ class AddUserContent extends React.Component<
selectedGroups: elements
});
}}
loading={loadingGroups}
records={groupsList}
/>
</Grid>
<Grid item xs={12}>

View File

@@ -39,8 +39,6 @@ interface IGroupsProps {
classes: any;
selectedGroups: string[];
setSelectedGroups: any;
records: any[];
loading: boolean;
}
const styles = (theme: Theme) =>

View File

@@ -16,35 +16,36 @@
import React from "react";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
import Paper from "@material-ui/core/Paper";
import Grid from "@material-ui/core/Grid";
import api from "../../../common/api";
import {
Button,
IconButton,
LinearProgress,
TableFooter,
TablePagination
TablePagination,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
Paper,
Grid,
Typography,
TextField,
InputAdornment
} from "@material-ui/core";
import Typography from "@material-ui/core/Typography";
import { User, UsersList } from "./types";
import { usersSort } from "../../../utils/sortFunctions";
import { MinTablePaginationActions } from "../../../common/MinTablePaginationActions";
import AddUser from "./AddUser";
import DeleteUser from "./DeleteUser";
import { CreateIcon } from "../../../icons";
import TextField from "@material-ui/core/TextField";
import InputAdornment from "@material-ui/core/InputAdornment";
import SearchIcon from "@material-ui/icons/Search";
import Checkbox from "@material-ui/core/Checkbox";
import DeleteIcon from "@material-ui/icons/Delete";
import ViewIcon from "@material-ui/icons/Visibility";
import GroupIcon from "@material-ui/icons/Group";
import { User, UsersList } from "./types";
import { usersSort } from "../../../utils/sortFunctions";
import { MinTablePaginationActions } from "../../../common/MinTablePaginationActions";
import { CreateIcon } from "../../../icons";
import AddUser from "./AddUser";
import DeleteUser from "./DeleteUser";
import AddToGroup from "./AddToGroup";
const styles = (theme: Theme) =>
createStyles({
@@ -112,6 +113,7 @@ interface IUsersState {
selectedUser: User | null;
addGroupOpen: boolean;
filter: string;
checkedUsers: string[];
}
class Users extends React.Component<IUsersProps, IUsersState> {
@@ -127,7 +129,8 @@ class Users extends React.Component<IUsersProps, IUsersState> {
deleteOpen: false,
selectedUser: null,
addGroupOpen: false,
filter: ""
filter: "",
checkedUsers: []
};
fetchRecords() {
@@ -176,6 +179,17 @@ class Users extends React.Component<IUsersProps, IUsersState> {
});
}
closeAddGroupBulk(unCheckAll: boolean = false) {
let newStates = { addGroupOpen: false };
let addUsers = {};
if (unCheckAll) {
addUsers = { checkedUsers: [] };
}
this.setState({ ...newStates, ...addUsers });
}
componentDidMount(): void {
this.fetchRecords();
}
@@ -191,7 +205,9 @@ class Users extends React.Component<IUsersProps, IUsersState> {
rowsPerPage,
deleteOpen,
selectedUser,
filter
filter,
checkedUsers,
addGroupOpen
} = this.state;
const handleChangePage = (event: unknown, newPage: number) => {
@@ -213,6 +229,28 @@ class Users extends React.Component<IUsersProps, IUsersState> {
elementItem.accessKey.includes(filter)
);
const selectionChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
const targetD = e.target;
const value = targetD.value;
const checked = targetD.checked;
let elements: string[] = [...checkedUsers]; // We clone the checkedUsers array
if (checked) {
// If the user has checked this field we need to push this to checkedUsersList
elements.push(value);
} else {
// User has unchecked this field, we need to remove it from the list
elements = elements.filter(element => element !== value);
}
this.setState({
checkedUsers: elements
});
return elements;
};
return (
<React.Fragment>
{addScreenOpen && (
@@ -233,6 +271,16 @@ class Users extends React.Component<IUsersProps, IUsersState> {
}}
/>
)}
{addGroupOpen && (
<AddToGroup
open={addGroupOpen}
checkedUsers={checkedUsers}
closeModalAndRefresh={(close: boolean) => {
this.closeAddGroupBulk(close);
}}
/>
)}
<Grid container>
<Grid item xs={12}>
<Typography variant="h6">Users</Typography>
@@ -262,10 +310,13 @@ class Users extends React.Component<IUsersProps, IUsersState> {
variant="contained"
color="primary"
startIcon={<GroupIcon />}
disabled={checkedUsers.length <= 0}
onClick={() => {
this.setState({
addGroupOpen: true
});
if (checkedUsers.length > 0) {
this.setState({
addGroupOpen: true
});
}
}}
>
Add to Group
@@ -305,9 +356,11 @@ class Users extends React.Component<IUsersProps, IUsersState> {
<TableRow key={`user-${row.accessKey}`}>
<TableCell padding="checkbox">
<Checkbox
value="secondary"
value={row.accessKey}
color="primary"
inputProps={{ "aria-label": "secondary checkbox" }}
checked={checkedUsers.includes(row.accessKey)}
onChange={selectionChanged}
/>
</TableCell>
<TableCell className={classes.wrapCell}>

View File

@@ -20,15 +20,15 @@ import (
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/swag"
"github.com/minio/mcs/restapi/operations"
"github.com/minio/mcs/restapi/operations/admin_api"
"github.com/minio/mcs/models"
"github.com/minio/mcs/restapi/operations"
"github.com/minio/mcs/restapi/operations/admin_api"
"github.com/minio/minio/pkg/madmin"
"context"
"fmt"
"log"
"strings"
)
func registerUsersHandlers(api *operations.McsAPI) {
@@ -83,6 +83,15 @@ func registerUsersHandlers(api *operations.McsAPI) {
return admin_api.NewUpdateUserInfoOK().WithPayload(userUpdateResponse)
})
// Update User-Groups Bulk
api.AdminAPIBulkUpdateUsersGroupsHandler = admin_api.BulkUpdateUsersGroupsHandlerFunc(func(params admin_api.BulkUpdateUsersGroupsParams, principal *models.Principal) middleware.Responder {
error := getAddUsersListToGroupsResponse(params)
if error != nil {
return admin_api.NewBulkUpdateUsersGroupsDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(error.Error())})
}
return admin_api.NewBulkUpdateUsersGroupsOK()
})
}
func listUsers(ctx context.Context, client MinioAdmin) ([]*models.User, error) {
@@ -402,3 +411,70 @@ func getUpdateUserResponse(params admin_api.UpdateUserInfoParams) (*models.User,
}
return userElem, nil
}
// addUsersListToGroups iterates over the user list & assigns the requested groups to each user.
func addUsersListToGroups(ctx context.Context, client MinioAdmin, usersToUpdate []string, groupsToAssign []string) error {
// We update each group with the complete usersList
parallelGroupsUpdate := func(groupToAssign string) chan error {
groupProcess := make(chan error)
go func() {
defer close(groupProcess)
// We add the users array to the group.
err := updateGroupMembers(ctx, client, groupToAssign, usersToUpdate, false)
groupProcess <- err
}()
return groupProcess
}
var groupsUpdateList []chan error
// We get each group name & add users accordingly
for _, groupName := range groupsToAssign {
// We update the group
proc := parallelGroupsUpdate(groupName)
groupsUpdateList = append(groupsUpdateList, proc)
}
errorsList := []string{} // We get the errors list because we want to have all errors at once.
for _, err := range groupsUpdateList {
errorFromUpdate := <-err // We store the error to avoid Data Race
if errorFromUpdate != nil {
// If there is an error, we store the errors strings so we can join them after we receive all errors
errorsList = append(errorsList, errorFromUpdate.Error()) // We wait until all the channels have been closed.
}
}
// If there are errors, we throw the final error with the errors inside
if len(errorsList) > 0 {
errGen := fmt.Errorf("error in users-groups assignation: %q", strings.Join(errorsList[:], ","))
return errGen
}
return nil
}
func getAddUsersListToGroupsResponse(params admin_api.BulkUpdateUsersGroupsParams) error {
ctx := context.Background()
mAdmin, err := newMAdminClient()
if err != nil {
log.Println("error creating Madmin Client:", err)
return err
}
// create a minioClient interface implementation
// defining the client to be used
adminClient := adminClient{client: mAdmin}
usersList := params.Body.Users
groupsList := params.Body.Groups
if err := addUsersListToGroups(ctx, adminClient, usersList, groupsList); err != nil {
log.Println("error updating groups bulk users:", err.Error())
return err
}
return nil
}

View File

@@ -258,7 +258,7 @@ func TestUserGroups(t *testing.T) {
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
}
// Test-2: updateUserGroups() make sure errors are handled correctly when error on DeleteUser()
// Test-2: updateUserGroups() make sure errors are handled correctly when error on UpdateGroupMembersMock()
// mock function response from removeUser(accessKey)
minioUpdateGroupMembersMock = func(remove madmin.GroupAddRemove) error {
@@ -368,3 +368,34 @@ func TestSetUserStatus(t *testing.T) {
assert.Equal("error", err.Error())
}
}
func TestUserGroupsBulk(t *testing.T) {
assert := asrt.New(t)
// mock minIO client
adminClient := adminClientMock{}
ctx := context.Background()
function := "updateUserGroups()"
mockUserGroups := []string{"group1", "group2", "group3"}
mockUsers := []string{"testUser", "testUser2"}
// Test-1: addUsersListToGroups() updates the groups for a users list
// mock function response from updateUserGroups(accessKey, groupsToAssign)
minioUpdateGroupMembersMock = func(remove madmin.GroupAddRemove) error {
return nil
}
if err := addUsersListToGroups(ctx, adminClient, mockUsers, mockUserGroups); err != nil {
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
}
// Test-2: addUsersListToGroups() make sure errors are handled correctly when error on updateGroupMembers()
// mock function response from removeUser(accessKey)
minioUpdateGroupMembersMock = func(remove madmin.GroupAddRemove) error {
return errors.New("error")
}
if err := addUsersListToGroups(ctx, adminClient, mockUsers, mockUserGroups); assert.Error(err) {
assert.Equal("error in users-groups assignation: \"error,error,error\"", err.Error())
}
}

View File

@@ -1096,6 +1096,36 @@ func init() {
}
}
},
"/users-groups-bulk": {
"put": {
"tags": [
"AdminAPI"
],
"summary": "Bulk functionality to Add Users to Groups",
"operationId": "BulkUpdateUsersGroups",
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/bulkUserGroups"
}
}
],
"responses": {
"200": {
"description": "A successful response."
},
"default": {
"description": "Generic error response.",
"schema": {
"$ref": "#/definitions/error"
}
}
}
}
},
"/users/{name}": {
"get": {
"tags": [
@@ -1356,6 +1386,27 @@ func init() {
}
}
},
"bulkUserGroups": {
"type": "object",
"required": [
"users",
"groups"
],
"properties": {
"groups": {
"type": "array",
"items": {
"type": "string"
}
},
"users": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"configDescription": {
"type": "object",
"properties": {
@@ -2948,6 +2999,36 @@ func init() {
}
}
},
"/users-groups-bulk": {
"put": {
"tags": [
"AdminAPI"
],
"summary": "Bulk functionality to Add Users to Groups",
"operationId": "BulkUpdateUsersGroups",
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/bulkUserGroups"
}
}
],
"responses": {
"200": {
"description": "A successful response."
},
"default": {
"description": "Generic error response.",
"schema": {
"$ref": "#/definitions/error"
}
}
}
}
},
"/users/{name}": {
"get": {
"tags": [
@@ -3208,6 +3289,27 @@ func init() {
}
}
},
"bulkUserGroups": {
"type": "object",
"required": [
"users",
"groups"
],
"properties": {
"groups": {
"type": "array",
"items": {
"type": "string"
}
},
"users": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"configDescription": {
"type": "object",
"properties": {

View File

@@ -0,0 +1,90 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 admin_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"net/http"
"github.com/go-openapi/runtime/middleware"
"github.com/minio/mcs/models"
)
// BulkUpdateUsersGroupsHandlerFunc turns a function with the right signature into a bulk update users groups handler
type BulkUpdateUsersGroupsHandlerFunc func(BulkUpdateUsersGroupsParams, *models.Principal) middleware.Responder
// Handle executing the request and returning a response
func (fn BulkUpdateUsersGroupsHandlerFunc) Handle(params BulkUpdateUsersGroupsParams, principal *models.Principal) middleware.Responder {
return fn(params, principal)
}
// BulkUpdateUsersGroupsHandler interface for that can handle valid bulk update users groups params
type BulkUpdateUsersGroupsHandler interface {
Handle(BulkUpdateUsersGroupsParams, *models.Principal) middleware.Responder
}
// NewBulkUpdateUsersGroups creates a new http.Handler for the bulk update users groups operation
func NewBulkUpdateUsersGroups(ctx *middleware.Context, handler BulkUpdateUsersGroupsHandler) *BulkUpdateUsersGroups {
return &BulkUpdateUsersGroups{Context: ctx, Handler: handler}
}
/*BulkUpdateUsersGroups swagger:route PUT /users-groups-bulk AdminAPI bulkUpdateUsersGroups
Bulk functionality to Add Users to Groups
*/
type BulkUpdateUsersGroups struct {
Context *middleware.Context
Handler BulkUpdateUsersGroupsHandler
}
func (o *BulkUpdateUsersGroups) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
route, rCtx, _ := o.Context.RouteInfo(r)
if rCtx != nil {
r = rCtx
}
var Params = NewBulkUpdateUsersGroupsParams()
uprinc, aCtx, err := o.Context.Authorize(r, route)
if err != nil {
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
if aCtx != nil {
r = aCtx
}
var principal *models.Principal
if uprinc != nil {
principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise
}
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
res := o.Handler.Handle(Params, principal) // actually handle the request
o.Context.Respond(rw, r, route.Produces, route, res)
}

View File

@@ -0,0 +1,94 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 admin_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"io"
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
"github.com/minio/mcs/models"
)
// NewBulkUpdateUsersGroupsParams creates a new BulkUpdateUsersGroupsParams object
// no default values defined in spec.
func NewBulkUpdateUsersGroupsParams() BulkUpdateUsersGroupsParams {
return BulkUpdateUsersGroupsParams{}
}
// BulkUpdateUsersGroupsParams contains all the bound params for the bulk update users groups operation
// typically these are obtained from a http.Request
//
// swagger:parameters BulkUpdateUsersGroups
type BulkUpdateUsersGroupsParams struct {
// HTTP Request Object
HTTPRequest *http.Request `json:"-"`
/*
Required: true
In: body
*/
Body *models.BulkUserGroups
}
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
// for simple values it will use straight method calls.
//
// To ensure default values, the struct must have been initialized with NewBulkUpdateUsersGroupsParams() beforehand.
func (o *BulkUpdateUsersGroupsParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
var res []error
o.HTTPRequest = r
if runtime.HasBody(r) {
defer r.Body.Close()
var body models.BulkUserGroups
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)
}
if len(res) == 0 {
o.Body = &body
}
}
} else {
res = append(res, errors.Required("body", "body"))
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}

View File

@@ -0,0 +1,113 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 admin_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/runtime"
"github.com/minio/mcs/models"
)
// BulkUpdateUsersGroupsOKCode is the HTTP code returned for type BulkUpdateUsersGroupsOK
const BulkUpdateUsersGroupsOKCode int = 200
/*BulkUpdateUsersGroupsOK A successful response.
swagger:response bulkUpdateUsersGroupsOK
*/
type BulkUpdateUsersGroupsOK struct {
}
// NewBulkUpdateUsersGroupsOK creates BulkUpdateUsersGroupsOK with default headers values
func NewBulkUpdateUsersGroupsOK() *BulkUpdateUsersGroupsOK {
return &BulkUpdateUsersGroupsOK{}
}
// WriteResponse to the client
func (o *BulkUpdateUsersGroupsOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
rw.WriteHeader(200)
}
/*BulkUpdateUsersGroupsDefault Generic error response.
swagger:response bulkUpdateUsersGroupsDefault
*/
type BulkUpdateUsersGroupsDefault struct {
_statusCode int
/*
In: Body
*/
Payload *models.Error `json:"body,omitempty"`
}
// NewBulkUpdateUsersGroupsDefault creates BulkUpdateUsersGroupsDefault with default headers values
func NewBulkUpdateUsersGroupsDefault(code int) *BulkUpdateUsersGroupsDefault {
if code <= 0 {
code = 500
}
return &BulkUpdateUsersGroupsDefault{
_statusCode: code,
}
}
// WithStatusCode adds the status to the bulk update users groups default response
func (o *BulkUpdateUsersGroupsDefault) WithStatusCode(code int) *BulkUpdateUsersGroupsDefault {
o._statusCode = code
return o
}
// SetStatusCode sets the status to the bulk update users groups default response
func (o *BulkUpdateUsersGroupsDefault) SetStatusCode(code int) {
o._statusCode = code
}
// WithPayload adds the payload to the bulk update users groups default response
func (o *BulkUpdateUsersGroupsDefault) WithPayload(payload *models.Error) *BulkUpdateUsersGroupsDefault {
o.Payload = payload
return o
}
// SetPayload sets the payload to the bulk update users groups default response
func (o *BulkUpdateUsersGroupsDefault) SetPayload(payload *models.Error) {
o.Payload = payload
}
// WriteResponse to the client
func (o *BulkUpdateUsersGroupsDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(o._statusCode)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
}

View File

@@ -0,0 +1,104 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 admin_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"errors"
"net/url"
golangswaggerpaths "path"
)
// BulkUpdateUsersGroupsURL generates an URL for the bulk update users groups operation
type BulkUpdateUsersGroupsURL struct {
_basePath string
}
// WithBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *BulkUpdateUsersGroupsURL) WithBasePath(bp string) *BulkUpdateUsersGroupsURL {
o.SetBasePath(bp)
return o
}
// SetBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *BulkUpdateUsersGroupsURL) SetBasePath(bp string) {
o._basePath = bp
}
// Build a url path and query string
func (o *BulkUpdateUsersGroupsURL) Build() (*url.URL, error) {
var _result url.URL
var _path = "/users-groups-bulk"
_basePath := o._basePath
if _basePath == "" {
_basePath = "/api/v1"
}
_result.Path = golangswaggerpaths.Join(_basePath, _path)
return &_result, nil
}
// Must is a helper function to panic when the url builder returns an error
func (o *BulkUpdateUsersGroupsURL) Must(u *url.URL, err error) *url.URL {
if err != nil {
panic(err)
}
if u == nil {
panic("url can't be nil")
}
return u
}
// String returns the string representation of the path with query string
func (o *BulkUpdateUsersGroupsURL) String() string {
return o.Must(o.Build()).String()
}
// BuildFull builds a full url with scheme, host, path and query string
func (o *BulkUpdateUsersGroupsURL) BuildFull(scheme, host string) (*url.URL, error) {
if scheme == "" {
return nil, errors.New("scheme is required for a full url on BulkUpdateUsersGroupsURL")
}
if host == "" {
return nil, errors.New("host is required for a full url on BulkUpdateUsersGroupsURL")
}
base, err := o.Build()
if err != nil {
return nil, err
}
base.Scheme = scheme
base.Host = host
return base, nil
}
// StringFull returns the string representation of a complete url
func (o *BulkUpdateUsersGroupsURL) StringFull(scheme, host string) string {
return o.Must(o.BuildFull(scheme, host)).String()
}

View File

@@ -0,0 +1,90 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"net/http"
"github.com/go-openapi/runtime/middleware"
"github.com/minio/mcs/models"
)
// BulkUpdateUsersGroupsHandlerFunc turns a function with the right signature into a bulk update users groups handler
type BulkUpdateUsersGroupsHandlerFunc func(BulkUpdateUsersGroupsParams, *models.Principal) middleware.Responder
// Handle executing the request and returning a response
func (fn BulkUpdateUsersGroupsHandlerFunc) Handle(params BulkUpdateUsersGroupsParams, principal *models.Principal) middleware.Responder {
return fn(params, principal)
}
// BulkUpdateUsersGroupsHandler interface for that can handle valid bulk update users groups params
type BulkUpdateUsersGroupsHandler interface {
Handle(BulkUpdateUsersGroupsParams, *models.Principal) middleware.Responder
}
// NewBulkUpdateUsersGroups creates a new http.Handler for the bulk update users groups operation
func NewBulkUpdateUsersGroups(ctx *middleware.Context, handler BulkUpdateUsersGroupsHandler) *BulkUpdateUsersGroups {
return &BulkUpdateUsersGroups{Context: ctx, Handler: handler}
}
/*BulkUpdateUsersGroups swagger:route PUT /user/groups bulkUpdateUsersGroups
Bulk functionality to Add Users to Groups
*/
type BulkUpdateUsersGroups struct {
Context *middleware.Context
Handler BulkUpdateUsersGroupsHandler
}
func (o *BulkUpdateUsersGroups) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
route, rCtx, _ := o.Context.RouteInfo(r)
if rCtx != nil {
r = rCtx
}
var Params = NewBulkUpdateUsersGroupsParams()
uprinc, aCtx, err := o.Context.Authorize(r, route)
if err != nil {
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
if aCtx != nil {
r = aCtx
}
var principal *models.Principal
if uprinc != nil {
principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise
}
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
res := o.Handler.Handle(Params, principal) // actually handle the request
o.Context.Respond(rw, r, route.Produces, route, res)
}

View File

@@ -0,0 +1,94 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"io"
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
"github.com/minio/mcs/models"
)
// NewBulkUpdateUsersGroupsParams creates a new BulkUpdateUsersGroupsParams object
// no default values defined in spec.
func NewBulkUpdateUsersGroupsParams() BulkUpdateUsersGroupsParams {
return BulkUpdateUsersGroupsParams{}
}
// BulkUpdateUsersGroupsParams contains all the bound params for the bulk update users groups operation
// typically these are obtained from a http.Request
//
// swagger:parameters BulkUpdateUsersGroups
type BulkUpdateUsersGroupsParams struct {
// HTTP Request Object
HTTPRequest *http.Request `json:"-"`
/*
Required: true
In: body
*/
Body *models.BulkUserGroups
}
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
// for simple values it will use straight method calls.
//
// To ensure default values, the struct must have been initialized with NewBulkUpdateUsersGroupsParams() beforehand.
func (o *BulkUpdateUsersGroupsParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
var res []error
o.HTTPRequest = r
if runtime.HasBody(r) {
defer r.Body.Close()
var body models.BulkUserGroups
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)
}
if len(res) == 0 {
o.Body = &body
}
}
} else {
res = append(res, errors.Required("body", "body"))
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}

View File

@@ -0,0 +1,113 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/runtime"
"github.com/minio/mcs/models"
)
// BulkUpdateUsersGroupsOKCode is the HTTP code returned for type BulkUpdateUsersGroupsOK
const BulkUpdateUsersGroupsOKCode int = 200
/*BulkUpdateUsersGroupsOK A successful response.
swagger:response bulkUpdateUsersGroupsOK
*/
type BulkUpdateUsersGroupsOK struct {
}
// NewBulkUpdateUsersGroupsOK creates BulkUpdateUsersGroupsOK with default headers values
func NewBulkUpdateUsersGroupsOK() *BulkUpdateUsersGroupsOK {
return &BulkUpdateUsersGroupsOK{}
}
// WriteResponse to the client
func (o *BulkUpdateUsersGroupsOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
rw.WriteHeader(200)
}
/*BulkUpdateUsersGroupsDefault Generic error response.
swagger:response bulkUpdateUsersGroupsDefault
*/
type BulkUpdateUsersGroupsDefault struct {
_statusCode int
/*
In: Body
*/
Payload *models.Error `json:"body,omitempty"`
}
// NewBulkUpdateUsersGroupsDefault creates BulkUpdateUsersGroupsDefault with default headers values
func NewBulkUpdateUsersGroupsDefault(code int) *BulkUpdateUsersGroupsDefault {
if code <= 0 {
code = 500
}
return &BulkUpdateUsersGroupsDefault{
_statusCode: code,
}
}
// WithStatusCode adds the status to the bulk update users groups default response
func (o *BulkUpdateUsersGroupsDefault) WithStatusCode(code int) *BulkUpdateUsersGroupsDefault {
o._statusCode = code
return o
}
// SetStatusCode sets the status to the bulk update users groups default response
func (o *BulkUpdateUsersGroupsDefault) SetStatusCode(code int) {
o._statusCode = code
}
// WithPayload adds the payload to the bulk update users groups default response
func (o *BulkUpdateUsersGroupsDefault) WithPayload(payload *models.Error) *BulkUpdateUsersGroupsDefault {
o.Payload = payload
return o
}
// SetPayload sets the payload to the bulk update users groups default response
func (o *BulkUpdateUsersGroupsDefault) SetPayload(payload *models.Error) {
o.Payload = payload
}
// WriteResponse to the client
func (o *BulkUpdateUsersGroupsDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(o._statusCode)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
}

View File

@@ -0,0 +1,104 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"errors"
"net/url"
golangswaggerpaths "path"
)
// BulkUpdateUsersGroupsURL generates an URL for the bulk update users groups operation
type BulkUpdateUsersGroupsURL struct {
_basePath string
}
// WithBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *BulkUpdateUsersGroupsURL) WithBasePath(bp string) *BulkUpdateUsersGroupsURL {
o.SetBasePath(bp)
return o
}
// SetBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *BulkUpdateUsersGroupsURL) SetBasePath(bp string) {
o._basePath = bp
}
// Build a url path and query string
func (o *BulkUpdateUsersGroupsURL) Build() (*url.URL, error) {
var _result url.URL
var _path = "/user/groups"
_basePath := o._basePath
if _basePath == "" {
_basePath = "/api/v1"
}
_result.Path = golangswaggerpaths.Join(_basePath, _path)
return &_result, nil
}
// Must is a helper function to panic when the url builder returns an error
func (o *BulkUpdateUsersGroupsURL) Must(u *url.URL, err error) *url.URL {
if err != nil {
panic(err)
}
if u == nil {
panic("url can't be nil")
}
return u
}
// String returns the string representation of the path with query string
func (o *BulkUpdateUsersGroupsURL) String() string {
return o.Must(o.Build()).String()
}
// BuildFull builds a full url with scheme, host, path and query string
func (o *BulkUpdateUsersGroupsURL) BuildFull(scheme, host string) (*url.URL, error) {
if scheme == "" {
return nil, errors.New("scheme is required for a full url on BulkUpdateUsersGroupsURL")
}
if host == "" {
return nil, errors.New("host is required for a full url on BulkUpdateUsersGroupsURL")
}
base, err := o.Build()
if err != nil {
return nil, err
}
base.Scheme = scheme
base.Host = host
return base, nil
}
// StringFull returns the string representation of a complete url
func (o *BulkUpdateUsersGroupsURL) StringFull(scheme, host string) string {
return o.Must(o.BuildFull(scheme, host)).String()
}

View File

@@ -87,6 +87,9 @@ func NewMcsAPI(spec *loads.Document) *McsAPI {
UserAPIBucketSetPolicyHandler: user_api.BucketSetPolicyHandlerFunc(func(params user_api.BucketSetPolicyParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation user_api.BucketSetPolicy has not yet been implemented")
}),
AdminAPIBulkUpdateUsersGroupsHandler: admin_api.BulkUpdateUsersGroupsHandlerFunc(func(params admin_api.BulkUpdateUsersGroupsParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation admin_api.BulkUpdateUsersGroups has not yet been implemented")
}),
AdminAPIConfigInfoHandler: admin_api.ConfigInfoHandlerFunc(func(params admin_api.ConfigInfoParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation admin_api.ConfigInfo has not yet been implemented")
}),
@@ -242,6 +245,8 @@ type McsAPI struct {
UserAPIBucketInfoHandler user_api.BucketInfoHandler
// UserAPIBucketSetPolicyHandler sets the operation handler for the bucket set policy operation
UserAPIBucketSetPolicyHandler user_api.BucketSetPolicyHandler
// AdminAPIBulkUpdateUsersGroupsHandler sets the operation handler for the bulk update users groups operation
AdminAPIBulkUpdateUsersGroupsHandler admin_api.BulkUpdateUsersGroupsHandler
// AdminAPIConfigInfoHandler sets the operation handler for the config info operation
AdminAPIConfigInfoHandler admin_api.ConfigInfoHandler
// UserAPICreateBucketEventHandler sets the operation handler for the create bucket event operation
@@ -399,6 +404,9 @@ func (o *McsAPI) Validate() error {
if o.UserAPIBucketSetPolicyHandler == nil {
unregistered = append(unregistered, "user_api.BucketSetPolicyHandler")
}
if o.AdminAPIBulkUpdateUsersGroupsHandler == nil {
unregistered = append(unregistered, "admin_api.BulkUpdateUsersGroupsHandler")
}
if o.AdminAPIConfigInfoHandler == nil {
unregistered = append(unregistered, "admin_api.ConfigInfoHandler")
}
@@ -621,6 +629,10 @@ func (o *McsAPI) initHandlerCache() {
o.handlers["PUT"] = make(map[string]http.Handler)
}
o.handlers["PUT"]["/buckets/{name}/set-policy"] = user_api.NewBucketSetPolicy(o.context, o.UserAPIBucketSetPolicyHandler)
if o.handlers["PUT"] == nil {
o.handlers["PUT"] = make(map[string]http.Handler)
}
o.handlers["PUT"]["/users-groups-bulk"] = admin_api.NewBulkUpdateUsersGroups(o.context, o.AdminAPIBulkUpdateUsersGroupsHandler)
if o.handlers["GET"] == nil {
o.handlers["GET"] = make(map[string]http.Handler)
}

View File

@@ -336,6 +336,25 @@ paths:
$ref: "#/definitions/error"
tags:
- AdminAPI
/users-groups-bulk:
put:
summary: Bulk functionality to Add Users to Groups
operationId: BulkUpdateUsersGroups
parameters:
- name: body
in: body
required: true
schema:
$ref: "#/definitions/bulkUserGroups"
responses:
200:
description: A successful response.
default:
description: Generic error response.
schema:
$ref: "#/definitions/error"
tags:
- AdminAPI
/groups:
get:
summary: List Groups
@@ -1230,4 +1249,17 @@ definitions:
type: array
items:
type: string
bulkUserGroups:
type: object
required:
- users
- groups
properties:
users:
type: array
items:
type: string
groups:
type: array
items:
type: string