Service Account Policy API (#1425)

* service account policy

* integration test

Co-authored-by: Alex <33497058+bexsoft@users.noreply.github.com>
Co-authored-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com>
This commit is contained in:
adfost
2022-01-31 13:37:00 -08:00
committed by GitHub
parent eb924ec842
commit 63d3c7207d
11 changed files with 768 additions and 0 deletions

View File

@@ -0,0 +1,168 @@
// This file is part of MinIO Console Server
// Copyright (c) 2022 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 integration
import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
"testing"
"time"
iampolicy "github.com/minio/pkg/iam/policy"
"github.com/stretchr/testify/assert"
)
func TestAddServiceAccount(t *testing.T) {
/*
This is an atomic API Test to add a user service account, the intention
is simple, add a user and make sure the response is 201 meaning that the
user got added successfully.
After test completion, it is expected that user is removed, so other
tests like users.ts can run over clean data and we don't collide against
it.
*/
assert := assert.New(t)
client := &http.Client{
Timeout: 3 * time.Second,
}
// Add service account
fmt.Println(".......................TestServiceAccountPolicy(): Create Data to add user")
requestDataAddServiceAccount := map[string]interface{}{
"accessKey": "testuser1",
"secretKey": "password",
"policy": "{" +
"\n \"Version\": \"2012-10-17\"," +
"\n \"Statement\": [" +
"\n {" +
"\n \"Effect\": \"Allow\"," +
"\n \"Action\": [" +
"\n \"s3:GetBucketLocation\"," +
"\n \"s3:GetObject\"" +
"\n ]," +
"\n \"Resource\": [" +
"\n \"arn:aws:s3:::*\"" +
"\n ]" +
"\n }" +
"\n ]" +
"\n}",
}
fmt.Println("..............................TestServiceAccountPolicy(): Prepare the POST")
requestDataJSON, _ := json.Marshal(requestDataAddServiceAccount)
requestDataBody := bytes.NewReader(requestDataJSON)
request, err := http.NewRequest(
"POST", "http://localhost:9090/api/v1/service-account-credentials", requestDataBody)
if err != nil {
log.Println(err)
return
}
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
request.Header.Add("Content-Type", "application/json")
fmt.Println(".................................TestServiceAccountPolicy(): Make the POST")
response, err := client.Do(request)
if err != nil {
log.Println(err)
return
}
fmt.Println("..................................TestServiceAccountPolicy(): Verification")
fmt.Println(".................................TestServiceAccountPolicy(): POST response")
fmt.Println(response)
fmt.Println("....................................TestServiceAccountPolicy(): POST error")
fmt.Println(err)
if response != nil {
fmt.Println("POST StatusCode:", response.StatusCode)
assert.Equal(201, response.StatusCode, "Status Code is incorrect")
}
fmt.Println("...................................TestServiceAccountPolicy(): Remove user")
// Test policy
fmt.Println(".......................TestAddUserServiceAccount(): Create Data to add user")
request, err = http.NewRequest(
"GET", "http://localhost:9090/api/v1/service-accounts/testuser1/policy", nil)
if err != nil {
log.Println(err)
return
}
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
request.Header.Add("Content-Type", "application/json")
fmt.Println(".................................TestAddServiceAccount(): Make the POST")
response, err = client.Do(request)
if err != nil {
log.Println(err)
return
}
fmt.Println("..................................TestAddServiceAccount(): Verification")
fmt.Println(".................................TestAddServiceAccount(): POST response")
fmt.Println(response)
fmt.Println("....................................TestAddServiceAccount(): POST error")
fmt.Println(err)
if response != nil {
fmt.Println("POST StatusCode:", response.StatusCode)
assert.Equal(200, response.StatusCode, "Status Code is incorrect")
buf := new(bytes.Buffer)
buf.ReadFrom(response.Body)
var actual *iampolicy.Policy
var expected *iampolicy.Policy
json.Unmarshal(buf.Bytes(), actual)
policy, err := json.Marshal(requestDataAddServiceAccount["policy"])
if err != nil {
log.Println(err)
return
}
json.Unmarshal(policy, expected)
assert.Equal(expected, actual)
}
fmt.Println("...................................TestServiceAccountPolicy(): Remove service account")
// {{baseUrl}}/user?name=proident velit
// Investiga como se borra en el browser.
request, err = http.NewRequest(
"DELETE", "http://localhost:9090/api/v1/service-accounts/testuser1", nil)
if err != nil {
log.Println(err)
return
}
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
request.Header.Add("Content-Type", "application/json")
fmt.Println("...............................TestServiceAccountPolicy(): Make the DELETE")
response, err = client.Do(request)
if err != nil {
log.Println(err)
return
}
fmt.Println("..................................TestServiceAccountPolicy(): Verification")
fmt.Println("...............................TestServiceAccountPolicy(): DELETE response")
fmt.Println(response)
fmt.Println("..................................TestServiceAccountPolicy(): DELETE error")
fmt.Println(err)
if response != nil {
fmt.Println("DELETE StatusCode:", response.StatusCode)
assert.Equal(204, response.StatusCode, "has to be 204 when delete user")
}
}

View File

@@ -98,6 +98,7 @@ type MinioAdmin interface {
addServiceAccount(ctx context.Context, policy *iampolicy.Policy, user string, accessKey string, secretKey string) (madmin.Credentials, error)
listServiceAccounts(ctx context.Context, user string) (madmin.ListServiceAccountsResp, error)
deleteServiceAccount(ctx context.Context, serviceAccount string) error
infoServiceAccount(ctx context.Context, serviceAccount string) (madmin.InfoServiceAccountResp, error)
// Remote Buckets
listRemoteBuckets(ctx context.Context, bucket, arnType string) (targets []madmin.BucketTarget, err error)
getRemoteBucket(ctx context.Context, bucket, arnType string) (targets *madmin.BucketTarget, err error)
@@ -306,6 +307,11 @@ func (ac AdminClient) deleteServiceAccount(ctx context.Context, serviceAccount s
return ac.Client.DeleteServiceAccount(ctx, serviceAccount)
}
// implements madmin.InfoServiceAccount()
func (ac AdminClient) infoServiceAccount(ctx context.Context, serviceAccount string) (madmin.InfoServiceAccountResp, error) {
return ac.Client.InfoServiceAccount(ctx, serviceAccount)
}
// AccountInfo implements madmin.AccountInfo()
func (ac AdminClient) AccountInfo(ctx context.Context) (madmin.AccountInfo, error) {
return ac.Client.AccountInfo(ctx, madmin.AccountOpts{})

View File

@@ -3090,6 +3090,37 @@ func init() {
}
}
},
"/service-accounts/{access_key}/policy": {
"get": {
"tags": [
"UserAPI"
],
"summary": "Get Service Account Policy",
"operationId": "GetServiceAccountPolicy",
"parameters": [
{
"type": "string",
"name": "access_key",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"type": "string"
}
},
"default": {
"description": "Generic error response.",
"schema": {
"$ref": "#/definitions/error"
}
}
}
}
},
"/service/restart": {
"post": {
"tags": [
@@ -9144,6 +9175,37 @@ func init() {
}
}
},
"/service-accounts/{access_key}/policy": {
"get": {
"tags": [
"UserAPI"
],
"summary": "Get Service Account Policy",
"operationId": "GetServiceAccountPolicy",
"parameters": [
{
"type": "string",
"name": "access_key",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"type": "string"
}
},
"default": {
"description": "Generic error response.",
"schema": {
"$ref": "#/definitions/error"
}
}
}
}
},
"/service/restart": {
"post": {
"tags": [

View File

@@ -197,6 +197,9 @@ func NewConsoleAPI(spec *loads.Document) *ConsoleAPI {
UserAPIGetObjectMetadataHandler: user_api.GetObjectMetadataHandlerFunc(func(params user_api.GetObjectMetadataParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation user_api.GetObjectMetadata has not yet been implemented")
}),
UserAPIGetServiceAccountPolicyHandler: user_api.GetServiceAccountPolicyHandlerFunc(func(params user_api.GetServiceAccountPolicyParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation user_api.GetServiceAccountPolicy has not yet been implemented")
}),
AdminAPIGetTierHandler: admin_api.GetTierHandlerFunc(func(params admin_api.GetTierParams, principal *models.Principal) middleware.Responder {
return middleware.NotImplemented("operation admin_api.GetTier has not yet been implemented")
}),
@@ -526,6 +529,8 @@ type ConsoleAPI struct {
UserAPIGetBucketVersioningHandler user_api.GetBucketVersioningHandler
// UserAPIGetObjectMetadataHandler sets the operation handler for the get object metadata operation
UserAPIGetObjectMetadataHandler user_api.GetObjectMetadataHandler
// UserAPIGetServiceAccountPolicyHandler sets the operation handler for the get service account policy operation
UserAPIGetServiceAccountPolicyHandler user_api.GetServiceAccountPolicyHandler
// AdminAPIGetTierHandler sets the operation handler for the get tier operation
AdminAPIGetTierHandler admin_api.GetTierHandler
// AdminAPIGetUserInfoHandler sets the operation handler for the get user info operation
@@ -869,6 +874,9 @@ func (o *ConsoleAPI) Validate() error {
if o.UserAPIGetObjectMetadataHandler == nil {
unregistered = append(unregistered, "user_api.GetObjectMetadataHandler")
}
if o.UserAPIGetServiceAccountPolicyHandler == nil {
unregistered = append(unregistered, "user_api.GetServiceAccountPolicyHandler")
}
if o.AdminAPIGetTierHandler == nil {
unregistered = append(unregistered, "admin_api.GetTierHandler")
}
@@ -1336,6 +1344,10 @@ func (o *ConsoleAPI) initHandlerCache() {
if o.handlers["GET"] == nil {
o.handlers["GET"] = make(map[string]http.Handler)
}
o.handlers["GET"]["/service-accounts/{access_key}/policy"] = user_api.NewGetServiceAccountPolicy(o.context, o.UserAPIGetServiceAccountPolicyHandler)
if o.handlers["GET"] == nil {
o.handlers["GET"] = make(map[string]http.Handler)
}
o.handlers["GET"]["/admin/tiers/{type}/{name}"] = admin_api.NewGetTier(o.context, o.AdminAPIGetTierHandler)
if o.handlers["GET"] == nil {
o.handlers["GET"] = make(map[string]http.Handler)

View File

@@ -0,0 +1,88 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 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/console/models"
)
// GetServiceAccountPolicyHandlerFunc turns a function with the right signature into a get service account policy handler
type GetServiceAccountPolicyHandlerFunc func(GetServiceAccountPolicyParams, *models.Principal) middleware.Responder
// Handle executing the request and returning a response
func (fn GetServiceAccountPolicyHandlerFunc) Handle(params GetServiceAccountPolicyParams, principal *models.Principal) middleware.Responder {
return fn(params, principal)
}
// GetServiceAccountPolicyHandler interface for that can handle valid get service account policy params
type GetServiceAccountPolicyHandler interface {
Handle(GetServiceAccountPolicyParams, *models.Principal) middleware.Responder
}
// NewGetServiceAccountPolicy creates a new http.Handler for the get service account policy operation
func NewGetServiceAccountPolicy(ctx *middleware.Context, handler GetServiceAccountPolicyHandler) *GetServiceAccountPolicy {
return &GetServiceAccountPolicy{Context: ctx, Handler: handler}
}
/* GetServiceAccountPolicy swagger:route GET /service-accounts/{access_key}/policy UserAPI getServiceAccountPolicy
Get Service Account Policy
*/
type GetServiceAccountPolicy struct {
Context *middleware.Context
Handler GetServiceAccountPolicyHandler
}
func (o *GetServiceAccountPolicy) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
route, rCtx, _ := o.Context.RouteInfo(r)
if rCtx != nil {
*r = *rCtx
}
var Params = NewGetServiceAccountPolicyParams()
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,88 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 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"
)
// NewGetServiceAccountPolicyParams creates a new GetServiceAccountPolicyParams object
//
// There are no default values defined in the spec.
func NewGetServiceAccountPolicyParams() GetServiceAccountPolicyParams {
return GetServiceAccountPolicyParams{}
}
// GetServiceAccountPolicyParams contains all the bound params for the get service account policy operation
// typically these are obtained from a http.Request
//
// swagger:parameters GetServiceAccountPolicy
type GetServiceAccountPolicyParams 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 NewGetServiceAccountPolicyParams() beforehand.
func (o *GetServiceAccountPolicyParams) 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 *GetServiceAccountPolicyParams) 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,131 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 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/console/models"
)
// GetServiceAccountPolicyOKCode is the HTTP code returned for type GetServiceAccountPolicyOK
const GetServiceAccountPolicyOKCode int = 200
/*GetServiceAccountPolicyOK A successful response.
swagger:response getServiceAccountPolicyOK
*/
type GetServiceAccountPolicyOK struct {
/*
In: Body
*/
Payload string `json:"body,omitempty"`
}
// NewGetServiceAccountPolicyOK creates GetServiceAccountPolicyOK with default headers values
func NewGetServiceAccountPolicyOK() *GetServiceAccountPolicyOK {
return &GetServiceAccountPolicyOK{}
}
// WithPayload adds the payload to the get service account policy o k response
func (o *GetServiceAccountPolicyOK) WithPayload(payload string) *GetServiceAccountPolicyOK {
o.Payload = payload
return o
}
// SetPayload sets the payload to the get service account policy o k response
func (o *GetServiceAccountPolicyOK) SetPayload(payload string) {
o.Payload = payload
}
// WriteResponse to the client
func (o *GetServiceAccountPolicyOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(200)
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
/*GetServiceAccountPolicyDefault Generic error response.
swagger:response getServiceAccountPolicyDefault
*/
type GetServiceAccountPolicyDefault struct {
_statusCode int
/*
In: Body
*/
Payload *models.Error `json:"body,omitempty"`
}
// NewGetServiceAccountPolicyDefault creates GetServiceAccountPolicyDefault with default headers values
func NewGetServiceAccountPolicyDefault(code int) *GetServiceAccountPolicyDefault {
if code <= 0 {
code = 500
}
return &GetServiceAccountPolicyDefault{
_statusCode: code,
}
}
// WithStatusCode adds the status to the get service account policy default response
func (o *GetServiceAccountPolicyDefault) WithStatusCode(code int) *GetServiceAccountPolicyDefault {
o._statusCode = code
return o
}
// SetStatusCode sets the status to the get service account policy default response
func (o *GetServiceAccountPolicyDefault) SetStatusCode(code int) {
o._statusCode = code
}
// WithPayload adds the payload to the get service account policy default response
func (o *GetServiceAccountPolicyDefault) WithPayload(payload *models.Error) *GetServiceAccountPolicyDefault {
o.Payload = payload
return o
}
// SetPayload sets the payload to the get service account policy default response
func (o *GetServiceAccountPolicyDefault) SetPayload(payload *models.Error) {
o.Payload = payload
}
// WriteResponse to the client
func (o *GetServiceAccountPolicyDefault) 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) 2021 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"
)
// GetServiceAccountPolicyURL generates an URL for the get service account policy operation
type GetServiceAccountPolicyURL 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 *GetServiceAccountPolicyURL) WithBasePath(bp string) *GetServiceAccountPolicyURL {
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 *GetServiceAccountPolicyURL) SetBasePath(bp string) {
o._basePath = bp
}
// Build a url path and query string
func (o *GetServiceAccountPolicyURL) Build() (*url.URL, error) {
var _result url.URL
var _path = "/service-accounts/{access_key}/policy"
accessKey := o.AccessKey
if accessKey != "" {
_path = strings.Replace(_path, "{access_key}", accessKey, -1)
} else {
return nil, errors.New("accessKey is required on GetServiceAccountPolicyURL")
}
_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 *GetServiceAccountPolicyURL) 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 *GetServiceAccountPolicyURL) String() string {
return o.Must(o.Build()).String()
}
// BuildFull builds a full url with scheme, host, path and query string
func (o *GetServiceAccountPolicyURL) BuildFull(scheme, host string) (*url.URL, error) {
if scheme == "" {
return nil, errors.New("scheme is required for a full url on GetServiceAccountPolicyURL")
}
if host == "" {
return nil, errors.New("host is required for a full url on GetServiceAccountPolicyURL")
}
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 *GetServiceAccountPolicyURL) StringFull(scheme, host string) string {
return o.Must(o.BuildFull(scheme, host)).String()
}

View File

@@ -20,6 +20,7 @@ import (
"bytes"
"context"
"errors"
"fmt"
"strings"
"time"
@@ -90,6 +91,14 @@ func registerServiceAccountsHandlers(api *operations.ConsoleAPI) {
return user_api.NewListUserServiceAccountsOK().WithPayload(serviceAccounts)
})
api.UserAPIGetServiceAccountPolicyHandler = user_api.GetServiceAccountPolicyHandlerFunc(func(params user_api.GetServiceAccountPolicyParams, session *models.Principal) middleware.Responder {
serviceAccounts, err := getServiceAccountPolicyResponse(session, params.AccessKey)
if err != nil {
return user_api.NewGetServiceAccountPolicyDefault(int(err.Code)).WithPayload(err)
}
return user_api.NewGetServiceAccountPolicyOK().WithPayload(serviceAccounts)
})
}
// createServiceAccount adds a service account to the userClient and assigns a policy to him if defined.
@@ -268,6 +277,7 @@ func getCreateServiceAccountCredsResponse(session *models.Principal, serviceAcco
}
accounts, err := userAdminClient.listServiceAccounts(ctx, "")
fmt.Println("dummy line")
if err != nil {
return nil, prepareError(err)
}
@@ -342,3 +352,33 @@ func getDeleteServiceAccountResponse(session *models.Principal, accessKey string
}
return nil
}
// getServiceAccountPolicy gets policy for a service account
func getServiceAccountPolicy(ctx context.Context, userClient MinioAdmin, accessKey string) (string, error) {
serviceAccountInfo, err := userClient.infoServiceAccount(ctx, accessKey)
if err != nil {
return "", err
}
return serviceAccountInfo.Policy, nil
}
// getServiceAccountPolicyResponse authenticates the user and calls
// getServiceAccountPolicy to get the policy for a service account
func getServiceAccountPolicyResponse(session *models.Principal, accessKey string) (string, *models.Error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
defer cancel()
userAdmin, err := NewMinioAdminClient(session)
if err != nil {
return "", prepareError(err)
}
// create a MinIO user Admin Client interface implementation
// defining the client to be used
userAdminClient := AdminClient{Client: userAdmin}
serviceAccounts, err := getServiceAccountPolicy(ctx, userAdminClient, accessKey)
if err != nil {
return "", prepareError(err)
}
return serviceAccounts, nil
}

View File

@@ -32,6 +32,7 @@ import (
var minioAddServiceAccountMock func(ctx context.Context, policy *iampolicy.Policy, user string, accessKey string, secretKey string) (madmin.Credentials, error)
var minioListServiceAccountsMock func(ctx context.Context, user string) (madmin.ListServiceAccountsResp, error)
var minioDeleteServiceAccountMock func(ctx context.Context, serviceAccount string) error
var minioInfoServiceAccountMock func(ctx context.Context, serviceAccount string) (madmin.InfoServiceAccountResp, error)
// mock function of AddServiceAccount()
func (ac adminClientMock) addServiceAccount(ctx context.Context, policy *iampolicy.Policy, user string, accessKey string, secretKey string) (madmin.Credentials, error) {
@@ -48,6 +49,11 @@ func (ac adminClientMock) deleteServiceAccount(ctx context.Context, serviceAccou
return minioDeleteServiceAccountMock(ctx, serviceAccount)
}
// mock function of InfoServiceAccount()
func (ac adminClientMock) infoServiceAccount(ctx context.Context, serviceAccount string) (madmin.InfoServiceAccountResp, error) {
return minioInfoServiceAccountMock(ctx, serviceAccount)
}
func TestAddServiceAccount(t *testing.T) {
assert := assert.New(t)
// mock minIO client
@@ -154,3 +160,33 @@ func TestDeleteServiceAccount(t *testing.T) {
assert.Equal("error", err.Error())
}
}
func TestGetServiceAccountPolicy(t *testing.T) {
assert := assert.New(t)
// mock minIO client
client := adminClientMock{}
function := "getServiceAccountPolicy()"
// Test-1: getServiceAccountPolicy list serviceaccounts for a user
ctx := context.Background()
mockResponse := madmin.InfoServiceAccountResp{
Policy: "{\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Effect\": \"Allow\",\n \"Action\": [\n \"s3:PutObject\"\n ],\n \"Resource\": [\n \"arn:aws:s3:::*\"\n ]\n }\n ]\n}",
}
minioInfoServiceAccountMock = func(ctx context.Context, user string) (madmin.InfoServiceAccountResp, error) {
return mockResponse, nil
}
serviceAccount, err := getServiceAccountPolicy(ctx, client, "")
if err != nil {
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
}
assert.Equal(mockResponse.Policy, serviceAccount)
// Test-2: getServiceAccountPolicy returns an error, handle it properly
minioInfoServiceAccountMock = func(ctx context.Context, user string) (madmin.InfoServiceAccountResp, error) {
return madmin.InfoServiceAccountResp{}, errors.New("error")
}
_, err = getServiceAccountPolicy(ctx, client, "")
if assert.Error(err) {
assert.Equal("error", err.Error())
}
}

View File

@@ -1260,6 +1260,27 @@ paths:
tags:
- UserAPI
/service-accounts/{access_key}/policy:
get:
summary: Get Service Account Policy
operationId: GetServiceAccountPolicy
parameters:
- name: access_key
in: path
required: true
type: string
responses:
200:
description: A successful response.
schema:
type: string
default:
description: Generic error response.
schema:
$ref: "#/definitions/error"
tags:
- UserAPI
/users:
get:
summary: List Users