Add list and delete service accounts api (#91)

This commit is contained in:
César Nieto
2020-05-04 15:48:38 -07:00
committed by GitHub
parent beb1ac7d04
commit 646318e1f6
20 changed files with 1369 additions and 44 deletions

View File

@@ -41,21 +41,21 @@ func registerAdminInfoHandlers(api *operations.McsAPI) {
}
type UsageInfo struct {
type usageInfo struct {
Buckets int64
Objects int64
Usage int64
}
// getAdminInfo invokes admin info and returns a parsed `UsageInfo` structure
func getAdminInfo(ctx context.Context, client MinioAdmin) (*UsageInfo, error) {
// getAdminInfo invokes admin info and returns a parsed `usageInfo` structure
func getAdminInfo(ctx context.Context, client MinioAdmin) (*usageInfo, error) {
serverInfo, err := client.serverInfo(ctx)
if err != nil {
return nil, err
}
// we are trimming uint64 to int64 this will report an incorrect measurement for numbers greater than
// 9,223,372,036,854,775,807
return &UsageInfo{
return &usageInfo{
Buckets: int64(serverInfo.Buckets.Count),
Objects: int64(serverInfo.Objects.Count),
Usage: int64(serverInfo.Usage.Size),

View File

@@ -83,6 +83,8 @@ type MinioAdmin interface {
serviceTrace(ctx context.Context, allTrace, errTrace bool) <-chan madmin.ServiceTraceInfo
// Service Accounts
addServiceAccount(ctx context.Context, policy *iampolicy.Policy) (mauth.Credentials, error)
listServiceAccounts(ctx context.Context) (madmin.ListServiceAccountsResp, error)
deleteServiceAccount(ctx context.Context, serviceAccount string) error
}
// Interface implementation
@@ -208,11 +210,30 @@ func (ac adminClient) addServiceAccount(ctx context.Context, policy *iampolicy.P
return ac.client.AddServiceAccount(ctx, policy)
}
// implements madmin.ListServiceAccounts()
func (ac adminClient) listServiceAccounts(ctx context.Context) (madmin.ListServiceAccountsResp, error) {
return ac.client.ListServiceAccounts(ctx)
}
// implements madmin.DeleteServiceAccount()
func (ac adminClient) deleteServiceAccount(ctx context.Context, serviceAccount string) error {
return ac.client.DeleteServiceAccount(ctx, serviceAccount)
}
func newMAdminClient(jwt string) (*madmin.AdminClient, error) {
claims, err := auth.JWTAuthenticate(jwt)
if err != nil {
return nil, err
}
adminClient, err := newAdminFromClaims(claims)
if err != nil {
return nil, err
}
return adminClient, nil
}
// newAdminFromClaims creates a minio admin from Decrypted claims using Assume role credentials
func newAdminFromClaims(claims *auth.DecryptedClaims) (*madmin.AdminClient, error) {
adminClient, err := madmin.NewWithOptions(getMinIOEndpoint(), &madmin.Options{
Creds: credentials.NewStaticV4(claims.AccessKeyID, claims.SecretAccessKey, claims.SessionToken),
Secure: getMinIOEndpointIsSecure(),

View File

@@ -993,6 +993,41 @@ func init() {
}
},
"/service-accounts": {
"get": {
"tags": [
"UserAPI"
],
"summary": "List User's Service Accounts",
"operationId": "ListUserServiceAccounts",
"parameters": [
{
"type": "integer",
"format": "int32",
"name": "offset",
"in": "query"
},
{
"type": "integer",
"format": "int32",
"name": "limit",
"in": "query"
}
],
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/serviceAccounts"
}
},
"default": {
"description": "Generic error response.",
"schema": {
"$ref": "#/definitions/error"
}
}
}
},
"post": {
"tags": [
"UserAPI"
@@ -1005,7 +1040,7 @@ func init() {
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/serviceAccount"
"$ref": "#/definitions/serviceAccountRequest"
}
}
],
@@ -1025,6 +1060,34 @@ func init() {
}
}
},
"/service-accounts/{access_key}": {
"delete": {
"tags": [
"UserAPI"
],
"summary": "Delete Service Account",
"operationId": "DeleteServiceAccount",
"parameters": [
{
"type": "string",
"name": "access_key",
"in": "path",
"required": true
}
],
"responses": {
"204": {
"description": "A successful response."
},
"default": {
"description": "Generic error response.",
"schema": {
"$ref": "#/definitions/error"
}
}
}
}
},
"/service/restart": {
"post": {
"tags": [
@@ -1883,15 +1946,6 @@ func init() {
}
}
},
"serviceAccount": {
"type": "object",
"properties": {
"policy": {
"type": "string",
"title": "policy to be applied to the Service Account if any"
}
}
},
"serviceAccountCreds": {
"type": "object",
"properties": {
@@ -1903,6 +1957,21 @@ func init() {
}
}
},
"serviceAccountRequest": {
"type": "object",
"properties": {
"policy": {
"type": "string",
"title": "policy to be applied to the Service Account if any"
}
}
},
"serviceAccounts": {
"type": "array",
"items": {
"type": "string"
}
},
"sessionResponse": {
"type": "object",
"properties": {
@@ -3033,6 +3102,41 @@ func init() {
}
},
"/service-accounts": {
"get": {
"tags": [
"UserAPI"
],
"summary": "List User's Service Accounts",
"operationId": "ListUserServiceAccounts",
"parameters": [
{
"type": "integer",
"format": "int32",
"name": "offset",
"in": "query"
},
{
"type": "integer",
"format": "int32",
"name": "limit",
"in": "query"
}
],
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/serviceAccounts"
}
},
"default": {
"description": "Generic error response.",
"schema": {
"$ref": "#/definitions/error"
}
}
}
},
"post": {
"tags": [
"UserAPI"
@@ -3045,7 +3149,7 @@ func init() {
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/serviceAccount"
"$ref": "#/definitions/serviceAccountRequest"
}
}
],
@@ -3065,6 +3169,34 @@ func init() {
}
}
},
"/service-accounts/{access_key}": {
"delete": {
"tags": [
"UserAPI"
],
"summary": "Delete Service Account",
"operationId": "DeleteServiceAccount",
"parameters": [
{
"type": "string",
"name": "access_key",
"in": "path",
"required": true
}
],
"responses": {
"204": {
"description": "A successful response."
},
"default": {
"description": "Generic error response.",
"schema": {
"$ref": "#/definitions/error"
}
}
}
}
},
"/service/restart": {
"post": {
"tags": [
@@ -3923,15 +4055,6 @@ func init() {
}
}
},
"serviceAccount": {
"type": "object",
"properties": {
"policy": {
"type": "string",
"title": "policy to be applied to the Service Account if any"
}
}
},
"serviceAccountCreds": {
"type": "object",
"properties": {
@@ -3943,6 +4066,21 @@ func init() {
}
}
},
"serviceAccountRequest": {
"type": "object",
"properties": {
"policy": {
"type": "string",
"title": "policy to be applied to the Service Account if any"
}
}
},
"serviceAccounts": {
"type": "array",
"items": {
"type": "string"
}
},
"sessionResponse": {
"type": "object",
"properties": {

View File

@@ -105,6 +105,9 @@ func NewMcsAPI(spec *loads.Document) *McsAPI {
UserAPIDeleteBucketEventHandler: user_api.DeleteBucketEventHandlerFunc(func(params user_api.DeleteBucketEventParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation user_api.DeleteBucketEvent has not yet been implemented")
}),
UserAPIDeleteServiceAccountHandler: user_api.DeleteServiceAccountHandlerFunc(func(params user_api.DeleteServiceAccountParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation user_api.DeleteServiceAccount has not yet been implemented")
}),
AdminAPIGetUserInfoHandler: admin_api.GetUserInfoHandlerFunc(func(params admin_api.GetUserInfoParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation admin_api.GetUserInfo has not yet been implemented")
}),
@@ -126,6 +129,9 @@ func NewMcsAPI(spec *loads.Document) *McsAPI {
AdminAPIListPoliciesHandler: admin_api.ListPoliciesHandlerFunc(func(params admin_api.ListPoliciesParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation admin_api.ListPolicies has not yet been implemented")
}),
UserAPIListUserServiceAccountsHandler: user_api.ListUserServiceAccountsHandlerFunc(func(params user_api.ListUserServiceAccountsParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation user_api.ListUserServiceAccounts has not yet been implemented")
}),
AdminAPIListUsersHandler: admin_api.ListUsersHandlerFunc(func(params admin_api.ListUsersParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation admin_api.ListUsers has not yet been implemented")
}),
@@ -263,6 +269,8 @@ type McsAPI struct {
UserAPIDeleteBucketHandler user_api.DeleteBucketHandler
// UserAPIDeleteBucketEventHandler sets the operation handler for the delete bucket event operation
UserAPIDeleteBucketEventHandler user_api.DeleteBucketEventHandler
// UserAPIDeleteServiceAccountHandler sets the operation handler for the delete service account operation
UserAPIDeleteServiceAccountHandler user_api.DeleteServiceAccountHandler
// AdminAPIGetUserInfoHandler sets the operation handler for the get user info operation
AdminAPIGetUserInfoHandler admin_api.GetUserInfoHandler
// AdminAPIGroupInfoHandler sets the operation handler for the group info operation
@@ -277,6 +285,8 @@ type McsAPI struct {
AdminAPIListGroupsHandler admin_api.ListGroupsHandler
// AdminAPIListPoliciesHandler sets the operation handler for the list policies operation
AdminAPIListPoliciesHandler admin_api.ListPoliciesHandler
// UserAPIListUserServiceAccountsHandler sets the operation handler for the list user service accounts operation
UserAPIListUserServiceAccountsHandler user_api.ListUserServiceAccountsHandler
// AdminAPIListUsersHandler sets the operation handler for the list users operation
AdminAPIListUsersHandler admin_api.ListUsersHandler
// UserAPILoginHandler sets the operation handler for the login operation
@@ -432,6 +442,9 @@ func (o *McsAPI) Validate() error {
if o.UserAPIDeleteBucketEventHandler == nil {
unregistered = append(unregistered, "user_api.DeleteBucketEventHandler")
}
if o.UserAPIDeleteServiceAccountHandler == nil {
unregistered = append(unregistered, "user_api.DeleteServiceAccountHandler")
}
if o.AdminAPIGetUserInfoHandler == nil {
unregistered = append(unregistered, "admin_api.GetUserInfoHandler")
}
@@ -453,6 +466,9 @@ func (o *McsAPI) Validate() error {
if o.AdminAPIListPoliciesHandler == nil {
unregistered = append(unregistered, "admin_api.ListPoliciesHandler")
}
if o.UserAPIListUserServiceAccountsHandler == nil {
unregistered = append(unregistered, "user_api.ListUserServiceAccountsHandler")
}
if o.AdminAPIListUsersHandler == nil {
unregistered = append(unregistered, "admin_api.ListUsersHandler")
}
@@ -669,6 +685,10 @@ func (o *McsAPI) initHandlerCache() {
o.handlers["DELETE"] = make(map[string]http.Handler)
}
o.handlers["DELETE"]["/buckets/{bucket_name}/events/{arn}"] = user_api.NewDeleteBucketEvent(o.context, o.UserAPIDeleteBucketEventHandler)
if o.handlers["DELETE"] == nil {
o.handlers["DELETE"] = make(map[string]http.Handler)
}
o.handlers["DELETE"]["/service-accounts/{access_key}"] = user_api.NewDeleteServiceAccount(o.context, o.UserAPIDeleteServiceAccountHandler)
if o.handlers["GET"] == nil {
o.handlers["GET"] = make(map[string]http.Handler)
}
@@ -700,6 +720,10 @@ func (o *McsAPI) initHandlerCache() {
if o.handlers["GET"] == nil {
o.handlers["GET"] = make(map[string]http.Handler)
}
o.handlers["GET"]["/service-accounts"] = user_api.NewListUserServiceAccounts(o.context, o.UserAPIListUserServiceAccountsHandler)
if o.handlers["GET"] == nil {
o.handlers["GET"] = make(map[string]http.Handler)
}
o.handlers["GET"]["/users"] = admin_api.NewListUsers(o.context, o.AdminAPIListUsersHandler)
if o.handlers["POST"] == nil {
o.handlers["POST"] = make(map[string]http.Handler)

View File

@@ -53,7 +53,7 @@ type CreateServiceAccountParams struct {
Required: true
In: body
*/
Body *models.ServiceAccount
Body *models.ServiceAccountRequest
}
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
@@ -67,7 +67,7 @@ func (o *CreateServiceAccountParams) BindRequest(r *http.Request, route *middlew
if runtime.HasBody(r) {
defer r.Body.Close()
var body models.ServiceAccount
var body models.ServiceAccountRequest
if err := route.Consumer.Consume(r.Body, &body); err != nil {
if err == io.EOF {
res = append(res, errors.Required("body", "body"))

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 user_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"
)
// DeleteServiceAccountHandlerFunc turns a function with the right signature into a delete service account handler
type DeleteServiceAccountHandlerFunc func(DeleteServiceAccountParams, *models.Principal) middleware.Responder
// Handle executing the request and returning a response
func (fn DeleteServiceAccountHandlerFunc) Handle(params DeleteServiceAccountParams, principal *models.Principal) middleware.Responder {
return fn(params, principal)
}
// DeleteServiceAccountHandler interface for that can handle valid delete service account params
type DeleteServiceAccountHandler interface {
Handle(DeleteServiceAccountParams, *models.Principal) middleware.Responder
}
// NewDeleteServiceAccount creates a new http.Handler for the delete service account operation
func NewDeleteServiceAccount(ctx *middleware.Context, handler DeleteServiceAccountHandler) *DeleteServiceAccount {
return &DeleteServiceAccount{Context: ctx, Handler: handler}
}
/*DeleteServiceAccount swagger:route DELETE /service-accounts/{access_key} UserAPI deleteServiceAccount
Delete Service Account
*/
type DeleteServiceAccount struct {
Context *middleware.Context
Handler DeleteServiceAccountHandler
}
func (o *DeleteServiceAccount) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
route, rCtx, _ := o.Context.RouteInfo(r)
if rCtx != nil {
r = rCtx
}
var Params = NewDeleteServiceAccountParams()
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,89 @@
// 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 user_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/errors"
"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/strfmt"
)
// NewDeleteServiceAccountParams creates a new DeleteServiceAccountParams object
// no default values defined in spec.
func NewDeleteServiceAccountParams() DeleteServiceAccountParams {
return DeleteServiceAccountParams{}
}
// DeleteServiceAccountParams contains all the bound params for the delete service account operation
// typically these are obtained from a http.Request
//
// swagger:parameters DeleteServiceAccount
type DeleteServiceAccountParams struct {
// HTTP Request Object
HTTPRequest *http.Request `json:"-"`
/*
Required: true
In: path
*/
AccessKey string
}
// 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 NewDeleteServiceAccountParams() beforehand.
func (o *DeleteServiceAccountParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
var res []error
o.HTTPRequest = r
rAccessKey, rhkAccessKey, _ := route.Params.GetOK("access_key")
if err := o.bindAccessKey(rAccessKey, rhkAccessKey, route.Formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// bindAccessKey binds and validates parameter AccessKey from path.
func (o *DeleteServiceAccountParams) bindAccessKey(rawData []string, hasKey bool, formats strfmt.Registry) error {
var raw string
if len(rawData) > 0 {
raw = rawData[len(rawData)-1]
}
// Required: true
// Parameter is provided by construction from the route
o.AccessKey = raw
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 user_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"
)
// DeleteServiceAccountNoContentCode is the HTTP code returned for type DeleteServiceAccountNoContent
const DeleteServiceAccountNoContentCode int = 204
/*DeleteServiceAccountNoContent A successful response.
swagger:response deleteServiceAccountNoContent
*/
type DeleteServiceAccountNoContent struct {
}
// NewDeleteServiceAccountNoContent creates DeleteServiceAccountNoContent with default headers values
func NewDeleteServiceAccountNoContent() *DeleteServiceAccountNoContent {
return &DeleteServiceAccountNoContent{}
}
// WriteResponse to the client
func (o *DeleteServiceAccountNoContent) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
rw.WriteHeader(204)
}
/*DeleteServiceAccountDefault Generic error response.
swagger:response deleteServiceAccountDefault
*/
type DeleteServiceAccountDefault struct {
_statusCode int
/*
In: Body
*/
Payload *models.Error `json:"body,omitempty"`
}
// NewDeleteServiceAccountDefault creates DeleteServiceAccountDefault with default headers values
func NewDeleteServiceAccountDefault(code int) *DeleteServiceAccountDefault {
if code <= 0 {
code = 500
}
return &DeleteServiceAccountDefault{
_statusCode: code,
}
}
// WithStatusCode adds the status to the delete service account default response
func (o *DeleteServiceAccountDefault) WithStatusCode(code int) *DeleteServiceAccountDefault {
o._statusCode = code
return o
}
// SetStatusCode sets the status to the delete service account default response
func (o *DeleteServiceAccountDefault) SetStatusCode(code int) {
o._statusCode = code
}
// WithPayload adds the payload to the delete service account default response
func (o *DeleteServiceAccountDefault) WithPayload(payload *models.Error) *DeleteServiceAccountDefault {
o.Payload = payload
return o
}
// SetPayload sets the payload to the delete service account default response
func (o *DeleteServiceAccountDefault) SetPayload(payload *models.Error) {
o.Payload = payload
}
// WriteResponse to the client
func (o *DeleteServiceAccountDefault) 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,116 @@
// 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 user_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"
"strings"
)
// DeleteServiceAccountURL generates an URL for the delete service account operation
type DeleteServiceAccountURL struct {
AccessKey string
_basePath string
// avoid unkeyed usage
_ struct{}
}
// 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 *DeleteServiceAccountURL) WithBasePath(bp string) *DeleteServiceAccountURL {
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 *DeleteServiceAccountURL) SetBasePath(bp string) {
o._basePath = bp
}
// Build a url path and query string
func (o *DeleteServiceAccountURL) Build() (*url.URL, error) {
var _result url.URL
var _path = "/service-accounts/{access_key}"
accessKey := o.AccessKey
if accessKey != "" {
_path = strings.Replace(_path, "{access_key}", accessKey, -1)
} else {
return nil, errors.New("accessKey is required on DeleteServiceAccountURL")
}
_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 *DeleteServiceAccountURL) 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 *DeleteServiceAccountURL) String() string {
return o.Must(o.Build()).String()
}
// BuildFull builds a full url with scheme, host, path and query string
func (o *DeleteServiceAccountURL) BuildFull(scheme, host string) (*url.URL, error) {
if scheme == "" {
return nil, errors.New("scheme is required for a full url on DeleteServiceAccountURL")
}
if host == "" {
return nil, errors.New("host is required for a full url on DeleteServiceAccountURL")
}
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 *DeleteServiceAccountURL) 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 user_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"
)
// ListUserServiceAccountsHandlerFunc turns a function with the right signature into a list user service accounts handler
type ListUserServiceAccountsHandlerFunc func(ListUserServiceAccountsParams, *models.Principal) middleware.Responder
// Handle executing the request and returning a response
func (fn ListUserServiceAccountsHandlerFunc) Handle(params ListUserServiceAccountsParams, principal *models.Principal) middleware.Responder {
return fn(params, principal)
}
// ListUserServiceAccountsHandler interface for that can handle valid list user service accounts params
type ListUserServiceAccountsHandler interface {
Handle(ListUserServiceAccountsParams, *models.Principal) middleware.Responder
}
// NewListUserServiceAccounts creates a new http.Handler for the list user service accounts operation
func NewListUserServiceAccounts(ctx *middleware.Context, handler ListUserServiceAccountsHandler) *ListUserServiceAccounts {
return &ListUserServiceAccounts{Context: ctx, Handler: handler}
}
/*ListUserServiceAccounts swagger:route GET /service-accounts UserAPI listUserServiceAccounts
List User's Service Accounts
*/
type ListUserServiceAccounts struct {
Context *middleware.Context
Handler ListUserServiceAccountsHandler
}
func (o *ListUserServiceAccounts) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
route, rCtx, _ := o.Context.RouteInfo(r)
if rCtx != nil {
r = rCtx
}
var Params = NewListUserServiceAccountsParams()
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,130 @@
// 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 user_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/errors"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
)
// NewListUserServiceAccountsParams creates a new ListUserServiceAccountsParams object
// no default values defined in spec.
func NewListUserServiceAccountsParams() ListUserServiceAccountsParams {
return ListUserServiceAccountsParams{}
}
// ListUserServiceAccountsParams contains all the bound params for the list user service accounts operation
// typically these are obtained from a http.Request
//
// swagger:parameters ListUserServiceAccounts
type ListUserServiceAccountsParams struct {
// HTTP Request Object
HTTPRequest *http.Request `json:"-"`
/*
In: query
*/
Limit *int32
/*
In: query
*/
Offset *int32
}
// 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 NewListUserServiceAccountsParams() beforehand.
func (o *ListUserServiceAccountsParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
var res []error
o.HTTPRequest = r
qs := runtime.Values(r.URL.Query())
qLimit, qhkLimit, _ := qs.GetOK("limit")
if err := o.bindLimit(qLimit, qhkLimit, route.Formats); err != nil {
res = append(res, err)
}
qOffset, qhkOffset, _ := qs.GetOK("offset")
if err := o.bindOffset(qOffset, qhkOffset, route.Formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// bindLimit binds and validates parameter Limit from query.
func (o *ListUserServiceAccountsParams) bindLimit(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
}
value, err := swag.ConvertInt32(raw)
if err != nil {
return errors.InvalidType("limit", "query", "int32", raw)
}
o.Limit = &value
return nil
}
// bindOffset binds and validates parameter Offset from query.
func (o *ListUserServiceAccountsParams) bindOffset(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
}
value, err := swag.ConvertInt32(raw)
if err != nil {
return errors.InvalidType("offset", "query", "int32", raw)
}
o.Offset = &value
return nil
}

View File

@@ -0,0 +1,136 @@
// 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 user_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"
)
// ListUserServiceAccountsOKCode is the HTTP code returned for type ListUserServiceAccountsOK
const ListUserServiceAccountsOKCode int = 200
/*ListUserServiceAccountsOK A successful response.
swagger:response listUserServiceAccountsOK
*/
type ListUserServiceAccountsOK struct {
/*
In: Body
*/
Payload models.ServiceAccounts `json:"body,omitempty"`
}
// NewListUserServiceAccountsOK creates ListUserServiceAccountsOK with default headers values
func NewListUserServiceAccountsOK() *ListUserServiceAccountsOK {
return &ListUserServiceAccountsOK{}
}
// WithPayload adds the payload to the list user service accounts o k response
func (o *ListUserServiceAccountsOK) WithPayload(payload models.ServiceAccounts) *ListUserServiceAccountsOK {
o.Payload = payload
return o
}
// SetPayload sets the payload to the list user service accounts o k response
func (o *ListUserServiceAccountsOK) SetPayload(payload models.ServiceAccounts) {
o.Payload = payload
}
// WriteResponse to the client
func (o *ListUserServiceAccountsOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(200)
payload := o.Payload
if payload == nil {
// return empty array
payload = models.ServiceAccounts{}
}
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
/*ListUserServiceAccountsDefault Generic error response.
swagger:response listUserServiceAccountsDefault
*/
type ListUserServiceAccountsDefault struct {
_statusCode int
/*
In: Body
*/
Payload *models.Error `json:"body,omitempty"`
}
// NewListUserServiceAccountsDefault creates ListUserServiceAccountsDefault with default headers values
func NewListUserServiceAccountsDefault(code int) *ListUserServiceAccountsDefault {
if code <= 0 {
code = 500
}
return &ListUserServiceAccountsDefault{
_statusCode: code,
}
}
// WithStatusCode adds the status to the list user service accounts default response
func (o *ListUserServiceAccountsDefault) WithStatusCode(code int) *ListUserServiceAccountsDefault {
o._statusCode = code
return o
}
// SetStatusCode sets the status to the list user service accounts default response
func (o *ListUserServiceAccountsDefault) SetStatusCode(code int) {
o._statusCode = code
}
// WithPayload adds the payload to the list user service accounts default response
func (o *ListUserServiceAccountsDefault) WithPayload(payload *models.Error) *ListUserServiceAccountsDefault {
o.Payload = payload
return o
}
// SetPayload sets the payload to the list user service accounts default response
func (o *ListUserServiceAccountsDefault) SetPayload(payload *models.Error) {
o.Payload = payload
}
// WriteResponse to the client
func (o *ListUserServiceAccountsDefault) 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,131 @@
// 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 user_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"
"github.com/go-openapi/swag"
)
// ListUserServiceAccountsURL generates an URL for the list user service accounts operation
type ListUserServiceAccountsURL struct {
Limit *int32
Offset *int32
_basePath string
// avoid unkeyed usage
_ struct{}
}
// 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 *ListUserServiceAccountsURL) WithBasePath(bp string) *ListUserServiceAccountsURL {
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 *ListUserServiceAccountsURL) SetBasePath(bp string) {
o._basePath = bp
}
// Build a url path and query string
func (o *ListUserServiceAccountsURL) Build() (*url.URL, error) {
var _result url.URL
var _path = "/service-accounts"
_basePath := o._basePath
if _basePath == "" {
_basePath = "/api/v1"
}
_result.Path = golangswaggerpaths.Join(_basePath, _path)
qs := make(url.Values)
var limitQ string
if o.Limit != nil {
limitQ = swag.FormatInt32(*o.Limit)
}
if limitQ != "" {
qs.Set("limit", limitQ)
}
var offsetQ string
if o.Offset != nil {
offsetQ = swag.FormatInt32(*o.Offset)
}
if offsetQ != "" {
qs.Set("offset", offsetQ)
}
_result.RawQuery = qs.Encode()
return &_result, nil
}
// Must is a helper function to panic when the url builder returns an error
func (o *ListUserServiceAccountsURL) 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 *ListUserServiceAccountsURL) String() string {
return o.Must(o.Build()).String()
}
// BuildFull builds a full url with scheme, host, path and query string
func (o *ListUserServiceAccountsURL) BuildFull(scheme, host string) (*url.URL, error) {
if scheme == "" {
return nil, errors.New("scheme is required for a full url on ListUserServiceAccountsURL")
}
if host == "" {
return nil, errors.New("host is required for a full url on ListUserServiceAccountsURL")
}
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 *ListUserServiceAccountsURL) StringFull(scheme, host string) string {
return o.Must(o.BuildFull(scheme, host)).String()
}

View File

@@ -41,6 +41,24 @@ func registerServiceAccountsHandlers(api *operations.McsAPI) {
}
return user_api.NewCreateServiceAccountCreated().WithPayload(creds)
})
// List Service Accounts for User
api.UserAPIListUserServiceAccountsHandler = user_api.ListUserServiceAccountsHandlerFunc(func(params user_api.ListUserServiceAccountsParams, principal *models.Principal) middleware.Responder {
sessionID := string(*principal)
serviceAccounts, err := getUserServiceAccountsResponse(sessionID)
if err != nil {
return user_api.NewListUserServiceAccountsDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
}
return user_api.NewListUserServiceAccountsOK().WithPayload(serviceAccounts)
})
// Delete a User's service account
api.UserAPIDeleteServiceAccountHandler = user_api.DeleteServiceAccountHandlerFunc(func(params user_api.DeleteServiceAccountParams, principal *models.Principal) middleware.Responder {
sessionID := string(*principal)
if err := getDeleteServiceAccountResponse(sessionID, params.AccessKey); err != nil {
return user_api.NewDeleteServiceAccountDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
}
return user_api.NewDeleteServiceAccountNoContent()
})
}
// createServiceAccount adds a service account to the userClient and assigns a policy to him if defined.
@@ -64,7 +82,7 @@ func createServiceAccount(ctx context.Context, userClient MinioAdmin, policy str
// getCreateServiceAccountResponse creates a service account with the defined policy for the user that
// is requestingit ,it first gets the credentials of the user and creates a client which is going to
// make the call to create the Service Account
func getCreateServiceAccountResponse(userSessionID string, serviceAccount *models.ServiceAccount) (*models.ServiceAccountCreds, error) {
func getCreateServiceAccountResponse(userSessionID string, serviceAccount *models.ServiceAccountRequest) (*models.ServiceAccountCreds, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
defer cancel()
@@ -84,3 +102,66 @@ func getCreateServiceAccountResponse(userSessionID string, serviceAccount *model
}
return saCreds, nil
}
// getUserServiceAccount gets list of the user's service accounts
func getUserServiceAccounts(ctx context.Context, userClient MinioAdmin) (models.ServiceAccounts, error) {
listServAccs, err := userClient.listServiceAccounts(ctx)
if err != nil {
return nil, err
}
serviceAccounts := models.ServiceAccounts{}
for _, acc := range listServAccs.Accounts {
serviceAccounts = append(serviceAccounts, acc)
}
return serviceAccounts, nil
}
// getUserServiceAccountsResponse authenticates the user and calls
// getUserServiceAccounts to list the user's service accounts
func getUserServiceAccountsResponse(userSessionID string) (models.ServiceAccounts, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
defer cancel()
userAdmin, err := newMAdminClient(userSessionID)
if err != nil {
log.Println("error creating user Client:", err)
return nil, err
}
// create a MinIO user Admin Client interface implementation
// defining the client to be used
userAdminClient := adminClient{client: userAdmin}
serviceAccounts, err := getUserServiceAccounts(ctx, userAdminClient)
if err != nil {
log.Println("error listing user's service account:", err)
return nil, err
}
return serviceAccounts, nil
}
// deleteServiceAccount calls delete service account api
func deleteServiceAccount(ctx context.Context, userClient MinioAdmin, accessKey string) error {
return userClient.deleteServiceAccount(ctx, accessKey)
}
// getDeleteServiceAccountResponse authenticates the user and calls deleteServiceAccount
func getDeleteServiceAccountResponse(userSessionID, accessKey string) error {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
defer cancel()
userAdmin, err := newMAdminClient(userSessionID)
if err != nil {
log.Println("error creating user Client:", err)
return err
}
// create a MinIO user Admin Client interface implementation
// defining the client to be used
userAdminClient := adminClient{client: userAdmin}
if err := deleteServiceAccount(ctx, userAdminClient, accessKey); err != nil {
log.Println("error deleting user's service account:", err)
return err
}
return nil
}

View File

@@ -25,17 +25,30 @@ import (
"github.com/minio/minio/pkg/auth"
iampolicy "github.com/minio/minio/pkg/iam/policy"
"github.com/minio/minio/pkg/madmin"
"github.com/stretchr/testify/assert"
)
// assigning mock at runtime instead of compile time
var minioAddServiceAccountMock func(ctx context.Context, policy *iampolicy.Policy) (auth.Credentials, error)
var minioListServiceAccountsMock func(ctx context.Context) (madmin.ListServiceAccountsResp, error)
var minioDeleteServiceAccountMock func(ctx context.Context, serviceAccount string) error
// mock function of listUsers()
// mock function of AddServiceAccount()
func (ac adminClientMock) addServiceAccount(ctx context.Context, policy *iampolicy.Policy) (auth.Credentials, error) {
return minioAddServiceAccountMock(ctx, policy)
}
// mock function of ListServiceAccounts()
func (ac adminClientMock) listServiceAccounts(ctx context.Context) (madmin.ListServiceAccountsResp, error) {
return minioListServiceAccountsMock(ctx)
}
// mock function of DeleteServiceAccount()
func (ac adminClientMock) deleteServiceAccount(ctx context.Context, serviceAccount string) error {
return minioDeleteServiceAccountMock(ctx, serviceAccount)
}
func TestAddServiceAccount(t *testing.T) {
assert := assert.New(t)
// mock minIO client
@@ -84,3 +97,61 @@ func TestAddServiceAccount(t *testing.T) {
assert.Equal("error", err.Error())
}
}
func TestListServiceAccounts(t *testing.T) {
assert := assert.New(t)
// mock minIO client
client := adminClientMock{}
function := "getUserServiceAccounts()"
// Test-1: getUserServiceAccounts list serviceaccounts for a user
ctx := context.Background()
mockResponse := madmin.ListServiceAccountsResp{
Accounts: []string{"accesskey1", "accesskey2"},
}
minioListServiceAccountsMock = func(ctx context.Context) (madmin.ListServiceAccountsResp, error) {
return mockResponse, nil
}
serviceAccounts, err := getUserServiceAccounts(ctx, client)
if err != nil {
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
}
for i, sa := range serviceAccounts {
assert.Equal(mockResponse.Accounts[i], sa)
}
// Test-2: getUserServiceAccounts returns an error, handle it properly
minioListServiceAccountsMock = func(ctx context.Context) (madmin.ListServiceAccountsResp, error) {
return madmin.ListServiceAccountsResp{}, errors.New("error")
}
_, err = getUserServiceAccounts(ctx, client)
if assert.Error(err) {
assert.Equal("error", err.Error())
}
}
func TestDeleteServiceAccount(t *testing.T) {
assert := assert.New(t)
// mock minIO client
client := adminClientMock{}
function := "deleteServiceAccount()"
ctx := context.Background()
// Test-1: deleteServiceAccount receive a service account to delete
testServiceAccount := "accesskeytest"
minioDeleteServiceAccountMock = func(ctx context.Context, serviceAccount string) error {
return nil
}
if err := deleteServiceAccount(ctx, client, testServiceAccount); err != nil {
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
}
// Test-2: if an invalid policy is assigned to the service account, this will raise an error
minioDeleteServiceAccountMock = func(ctx context.Context, serviceAccount string) error {
return errors.New("error")
}
if err := deleteServiceAccount(ctx, client, testServiceAccount); assert.Error(err) {
assert.Equal("error", err.Error())
}
}