github.com/minio/madmin-go/v2@v2.2.1/policy-commands.go (about)

     1  //
     2  // Copyright (c) 2015-2022 MinIO, Inc.
     3  //
     4  // This file is part of MinIO Object Storage stack
     5  //
     6  // This program is free software: you can redistribute it and/or modify
     7  // it under the terms of the GNU Affero General Public License as
     8  // published by the Free Software Foundation, either version 3 of the
     9  // License, or (at your option) any later version.
    10  //
    11  // This program is distributed in the hope that it will be useful,
    12  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  // GNU Affero General Public License for more details.
    15  //
    16  // You should have received a copy of the GNU Affero General Public License
    17  // along with this program. If not, see <http://www.gnu.org/licenses/>.
    18  //
    19  
    20  package madmin
    21  
    22  import (
    23  	"context"
    24  	"encoding/json"
    25  	"io/ioutil"
    26  	"net/http"
    27  	"net/url"
    28  	"time"
    29  )
    30  
    31  // InfoCannedPolicy - expand canned policy into JSON structure.
    32  //
    33  // To be DEPRECATED in favor of the implementation in InfoCannedPolicyV2
    34  func (adm *AdminClient) InfoCannedPolicy(ctx context.Context, policyName string) ([]byte, error) {
    35  	queryValues := url.Values{}
    36  	queryValues.Set("name", policyName)
    37  
    38  	reqData := requestData{
    39  		relPath:     adminAPIPrefix + "/info-canned-policy",
    40  		queryValues: queryValues,
    41  	}
    42  
    43  	// Execute GET on /minio/admin/v3/info-canned-policy
    44  	resp, err := adm.executeMethod(ctx, http.MethodGet, reqData)
    45  
    46  	defer closeResponse(resp)
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  
    51  	if resp.StatusCode != http.StatusOK {
    52  		return nil, httpRespToErrorResponse(resp)
    53  	}
    54  
    55  	return ioutil.ReadAll(resp.Body)
    56  }
    57  
    58  // PolicyInfo contains information on a policy.
    59  type PolicyInfo struct {
    60  	PolicyName string
    61  	Policy     json.RawMessage
    62  	CreateDate time.Time `json:",omitempty"`
    63  	UpdateDate time.Time `json:",omitempty"`
    64  }
    65  
    66  // MarshalJSON marshaller for JSON
    67  func (pi PolicyInfo) MarshalJSON() ([]byte, error) {
    68  	type aliasPolicyInfo PolicyInfo // needed to avoid recursive marshal
    69  	if pi.CreateDate.IsZero() && pi.UpdateDate.IsZero() {
    70  		return json.Marshal(&struct {
    71  			PolicyName string
    72  			Policy     json.RawMessage
    73  		}{
    74  			PolicyName: pi.PolicyName,
    75  			Policy:     pi.Policy,
    76  		})
    77  	}
    78  	return json.Marshal(aliasPolicyInfo(pi))
    79  }
    80  
    81  // InfoCannedPolicyV2 - get info on a policy including timestamps and policy json.
    82  func (adm *AdminClient) InfoCannedPolicyV2(ctx context.Context, policyName string) (*PolicyInfo, error) {
    83  	queryValues := url.Values{}
    84  	queryValues.Set("name", policyName)
    85  	queryValues.Set("v", "2")
    86  
    87  	reqData := requestData{
    88  		relPath:     adminAPIPrefix + "/info-canned-policy",
    89  		queryValues: queryValues,
    90  	}
    91  
    92  	// Execute GET on /minio/admin/v3/info-canned-policy
    93  	resp, err := adm.executeMethod(ctx, http.MethodGet, reqData)
    94  
    95  	defer closeResponse(resp)
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  
   100  	if resp.StatusCode != http.StatusOK {
   101  		return nil, httpRespToErrorResponse(resp)
   102  	}
   103  
   104  	data, err := ioutil.ReadAll(resp.Body)
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  
   109  	var p PolicyInfo
   110  	err = json.Unmarshal(data, &p)
   111  	return &p, err
   112  }
   113  
   114  // ListCannedPolicies - list all configured canned policies.
   115  func (adm *AdminClient) ListCannedPolicies(ctx context.Context) (map[string]json.RawMessage, error) {
   116  	reqData := requestData{
   117  		relPath: adminAPIPrefix + "/list-canned-policies",
   118  	}
   119  
   120  	// Execute GET on /minio/admin/v3/list-canned-policies
   121  	resp, err := adm.executeMethod(ctx, http.MethodGet, reqData)
   122  
   123  	defer closeResponse(resp)
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  
   128  	if resp.StatusCode != http.StatusOK {
   129  		return nil, httpRespToErrorResponse(resp)
   130  	}
   131  
   132  	respBytes, err := ioutil.ReadAll(resp.Body)
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  
   137  	policies := make(map[string]json.RawMessage)
   138  	if err = json.Unmarshal(respBytes, &policies); err != nil {
   139  		return nil, err
   140  	}
   141  
   142  	return policies, nil
   143  }
   144  
   145  // RemoveCannedPolicy - remove a policy for a canned.
   146  func (adm *AdminClient) RemoveCannedPolicy(ctx context.Context, policyName string) error {
   147  	queryValues := url.Values{}
   148  	queryValues.Set("name", policyName)
   149  
   150  	reqData := requestData{
   151  		relPath:     adminAPIPrefix + "/remove-canned-policy",
   152  		queryValues: queryValues,
   153  	}
   154  
   155  	// Execute DELETE on /minio/admin/v3/remove-canned-policy to remove policy.
   156  	resp, err := adm.executeMethod(ctx, http.MethodDelete, reqData)
   157  
   158  	defer closeResponse(resp)
   159  	if err != nil {
   160  		return err
   161  	}
   162  
   163  	if resp.StatusCode != http.StatusOK {
   164  		return httpRespToErrorResponse(resp)
   165  	}
   166  
   167  	return nil
   168  }
   169  
   170  // AddCannedPolicy - adds a policy for a canned.
   171  func (adm *AdminClient) AddCannedPolicy(ctx context.Context, policyName string, policy []byte) error {
   172  	if policy == nil {
   173  		return ErrInvalidArgument("policy input cannot be empty")
   174  	}
   175  
   176  	queryValues := url.Values{}
   177  	queryValues.Set("name", policyName)
   178  
   179  	reqData := requestData{
   180  		relPath:     adminAPIPrefix + "/add-canned-policy",
   181  		queryValues: queryValues,
   182  		content:     policy,
   183  	}
   184  
   185  	// Execute PUT on /minio/admin/v3/add-canned-policy to set policy.
   186  	resp, err := adm.executeMethod(ctx, http.MethodPut, reqData)
   187  
   188  	defer closeResponse(resp)
   189  	if err != nil {
   190  		return err
   191  	}
   192  
   193  	if resp.StatusCode != http.StatusOK {
   194  		return httpRespToErrorResponse(resp)
   195  	}
   196  
   197  	return nil
   198  }
   199  
   200  // SetPolicy - sets the policy for a user or a group.
   201  func (adm *AdminClient) SetPolicy(ctx context.Context, policyName, entityName string, isGroup bool) error {
   202  	queryValues := url.Values{}
   203  	queryValues.Set("policyName", policyName)
   204  	queryValues.Set("userOrGroup", entityName)
   205  	groupStr := "false"
   206  	if isGroup {
   207  		groupStr = "true"
   208  	}
   209  	queryValues.Set("isGroup", groupStr)
   210  
   211  	reqData := requestData{
   212  		relPath:     adminAPIPrefix + "/set-user-or-group-policy",
   213  		queryValues: queryValues,
   214  	}
   215  
   216  	// Execute PUT on /minio/admin/v3/set-user-or-group-policy to set policy.
   217  	resp, err := adm.executeMethod(ctx, http.MethodPut, reqData)
   218  	defer closeResponse(resp)
   219  	if err != nil {
   220  		return err
   221  	}
   222  
   223  	if resp.StatusCode != http.StatusOK {
   224  		return httpRespToErrorResponse(resp)
   225  	}
   226  	return nil
   227  }
   228  
   229  func (adm *AdminClient) attachOrDetachPolicyBuiltin(ctx context.Context, isAttach bool, r PolicyAssociationReq) error {
   230  	err := r.IsValid()
   231  	if err != nil {
   232  		return err
   233  	}
   234  
   235  	plainBytes, err := json.Marshal(r)
   236  	if err != nil {
   237  		return err
   238  	}
   239  
   240  	encBytes, err := EncryptData(adm.getSecretKey(), plainBytes)
   241  	if err != nil {
   242  		return err
   243  	}
   244  
   245  	suffix := "detach"
   246  	if isAttach {
   247  		suffix = "attach"
   248  	}
   249  
   250  	reqData := requestData{
   251  		relPath: adminAPIPrefix + "/idp/builtin/policy/" + suffix,
   252  		content: encBytes,
   253  	}
   254  
   255  	resp, err := adm.executeMethod(ctx, http.MethodPost, reqData)
   256  	defer closeResponse(resp)
   257  	if err != nil {
   258  		return err
   259  	}
   260  
   261  	if (isAttach && resp.StatusCode != http.StatusCreated) ||
   262  		(!isAttach && resp.StatusCode != http.StatusNoContent) {
   263  		return httpRespToErrorResponse(resp)
   264  	}
   265  
   266  	return nil
   267  }
   268  
   269  // AttachPolicy - attach policies to a user or group.
   270  func (adm *AdminClient) AttachPolicy(ctx context.Context, r PolicyAssociationReq) error {
   271  	return adm.attachOrDetachPolicyBuiltin(ctx, true, r)
   272  }
   273  
   274  // DetachPolicy - detach policies from a user or group.
   275  func (adm *AdminClient) DetachPolicy(ctx context.Context, r PolicyAssociationReq) error {
   276  	return adm.attachOrDetachPolicyBuiltin(ctx, false, r)
   277  }
   278  
   279  // GetPolicyEntities - returns builtin policy entities.
   280  func (adm *AdminClient) GetPolicyEntities(ctx context.Context, q PolicyEntitiesQuery) (r PolicyEntitiesResult, err error) {
   281  	params := make(url.Values)
   282  	params["user"] = q.Users
   283  	params["group"] = q.Groups
   284  	params["policy"] = q.Policy
   285  
   286  	reqData := requestData{
   287  		relPath:     adminAPIPrefix + "/idp/builtin/policy-entities",
   288  		queryValues: params,
   289  	}
   290  
   291  	resp, err := adm.executeMethod(ctx, http.MethodGet, reqData)
   292  	defer closeResponse(resp)
   293  	if err != nil {
   294  		return r, err
   295  	}
   296  
   297  	if resp.StatusCode != http.StatusOK {
   298  		return r, httpRespToErrorResponse(resp)
   299  	}
   300  
   301  	content, err := DecryptData(adm.getSecretKey(), resp.Body)
   302  	if err != nil {
   303  		return r, err
   304  	}
   305  
   306  	err = json.Unmarshal(content, &r)
   307  	return r, err
   308  }