github.com/minio/madmin-go@v1.7.5/idp-commands.go (about)

     1  //
     2  // MinIO Object Storage (c) 2015-2022 MinIO, Inc.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //      http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  //
    16  
    17  package madmin
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"net/http"
    24  	"net/url"
    25  	"strings"
    26  	"time"
    27  
    28  	"github.com/minio/minio-go/v7/pkg/set"
    29  )
    30  
    31  // AddOrUpdateIDPConfig - creates a new or updates an existing IDP
    32  // configuration on the server.
    33  func (adm *AdminClient) AddOrUpdateIDPConfig(ctx context.Context, cfgType, cfgName, cfgData string, update bool) (restart bool, err error) {
    34  	encBytes, err := EncryptData(adm.getSecretKey(), []byte(cfgData))
    35  	if err != nil {
    36  		return false, err
    37  	}
    38  
    39  	method := http.MethodPut
    40  	if update {
    41  		method = http.MethodPost
    42  	}
    43  
    44  	if cfgName == "" {
    45  		cfgName = Default
    46  	}
    47  
    48  	h := make(http.Header, 1)
    49  	h.Add("Content-Type", "application/octet-stream")
    50  	reqData := requestData{
    51  		customHeaders: h,
    52  		relPath:       strings.Join([]string{adminAPIPrefix, "idp-config", cfgType, cfgName}, "/"),
    53  		content:       encBytes,
    54  	}
    55  
    56  	resp, err := adm.executeMethod(ctx, method, reqData)
    57  	defer closeResponse(resp)
    58  	if err != nil {
    59  		return false, err
    60  	}
    61  
    62  	// FIXME: Remove support for this older API in 2023-04 (about 6 months).
    63  	//
    64  	// Attempt to fall back to older IDP API.
    65  	if resp.StatusCode == http.StatusUpgradeRequired {
    66  		// close old response
    67  		closeResponse(resp)
    68  
    69  		// Fallback is needed for `mc admin idp set myminio openid ...` only, as
    70  		// this was the only released API supported in the older version.
    71  
    72  		queryParams := make(url.Values, 2)
    73  		queryParams.Set("type", cfgType)
    74  		queryParams.Set("name", cfgName)
    75  		reqData := requestData{
    76  			customHeaders: h,
    77  			relPath:       adminAPIPrefix + "/idp-config",
    78  			queryValues:   queryParams,
    79  			content:       encBytes,
    80  		}
    81  		resp, err = adm.executeMethod(ctx, http.MethodPut, reqData)
    82  		defer closeResponse(resp)
    83  		if err != nil {
    84  			return false, err
    85  		}
    86  	}
    87  
    88  	if resp.StatusCode != http.StatusOK {
    89  		return false, httpRespToErrorResponse(resp)
    90  	}
    91  
    92  	return resp.Header.Get(ConfigAppliedHeader) != ConfigAppliedTrue, nil
    93  }
    94  
    95  // IDPCfgInfo represents a single configuration or related parameter
    96  type IDPCfgInfo struct {
    97  	Key   string `json:"key"`
    98  	Value string `json:"value"`
    99  	IsCfg bool   `json:"isCfg"`
   100  	IsEnv bool   `json:"isEnv"` // relevant only when isCfg=true
   101  }
   102  
   103  // IDPConfig contains IDP configuration information returned by server.
   104  type IDPConfig struct {
   105  	Type string       `json:"type"`
   106  	Name string       `json:"name,omitempty"`
   107  	Info []IDPCfgInfo `json:"info"`
   108  }
   109  
   110  // Constants for IDP configuration types.
   111  const (
   112  	OpenidIDPCfg string = "openid"
   113  	LDAPIDPCfg   string = "ldap"
   114  )
   115  
   116  // ValidIDPConfigTypes - set of valid IDP configs.
   117  var ValidIDPConfigTypes = set.CreateStringSet(OpenidIDPCfg, LDAPIDPCfg)
   118  
   119  // GetIDPConfig - fetch IDP config from server.
   120  func (adm *AdminClient) GetIDPConfig(ctx context.Context, cfgType, cfgName string) (c IDPConfig, err error) {
   121  	if !ValidIDPConfigTypes.Contains(cfgType) {
   122  		return c, fmt.Errorf("Invalid config type: %s", cfgType)
   123  	}
   124  
   125  	if cfgName == "" {
   126  		cfgName = Default
   127  	}
   128  
   129  	reqData := requestData{
   130  		relPath: strings.Join([]string{adminAPIPrefix, "idp-config", cfgType, cfgName}, "/"),
   131  	}
   132  
   133  	resp, err := adm.executeMethod(ctx, http.MethodGet, reqData)
   134  	defer closeResponse(resp)
   135  	if err != nil {
   136  		return c, err
   137  	}
   138  
   139  	// FIXME: Remove support for this older API in 2023-04 (about 6 months).
   140  	//
   141  	// Attempt to fall back to older IDP API.
   142  	if resp.StatusCode == http.StatusUpgradeRequired {
   143  		// close old response
   144  		closeResponse(resp)
   145  
   146  		queryParams := make(url.Values, 2)
   147  		queryParams.Set("type", cfgType)
   148  		queryParams.Set("name", cfgName)
   149  		reqData := requestData{
   150  			relPath:     adminAPIPrefix + "/idp-config",
   151  			queryValues: queryParams,
   152  		}
   153  		resp, err = adm.executeMethod(ctx, http.MethodGet, reqData)
   154  		defer closeResponse(resp)
   155  		if err != nil {
   156  			return c, err
   157  		}
   158  	}
   159  
   160  	if resp.StatusCode != http.StatusOK {
   161  		return c, httpRespToErrorResponse(resp)
   162  	}
   163  
   164  	content, err := DecryptData(adm.getSecretKey(), resp.Body)
   165  	if err != nil {
   166  		return c, err
   167  	}
   168  
   169  	err = json.Unmarshal(content, &c)
   170  	return c, err
   171  }
   172  
   173  // IDPListItem - represents an item in the List IDPs call.
   174  type IDPListItem struct {
   175  	Type    string `json:"type"`
   176  	Name    string `json:"name"`
   177  	Enabled bool   `json:"enabled"`
   178  	RoleARN string `json:"roleARN,omitempty"`
   179  }
   180  
   181  // ListIDPConfig - list IDP configuration on the server.
   182  func (adm *AdminClient) ListIDPConfig(ctx context.Context, cfgType string) ([]IDPListItem, error) {
   183  	if !ValidIDPConfigTypes.Contains(cfgType) {
   184  		return nil, fmt.Errorf("Invalid config type: %s", cfgType)
   185  	}
   186  
   187  	reqData := requestData{
   188  		relPath: strings.Join([]string{adminAPIPrefix, "idp-config", cfgType}, "/"),
   189  	}
   190  
   191  	resp, err := adm.executeMethod(ctx, http.MethodGet, reqData)
   192  	defer closeResponse(resp)
   193  	if err != nil {
   194  		return nil, err
   195  	}
   196  
   197  	// FIXME: Remove support for this older API in 2023-04 (about 6 months).
   198  	//
   199  	// Attempt to fall back to older IDP API.
   200  	if resp.StatusCode == http.StatusUpgradeRequired {
   201  		// close old response
   202  		closeResponse(resp)
   203  
   204  		queryParams := make(url.Values, 2)
   205  		queryParams.Set("type", cfgType)
   206  		reqData := requestData{
   207  			relPath:     adminAPIPrefix + "/idp-config",
   208  			queryValues: queryParams,
   209  		}
   210  		resp, err = adm.executeMethod(ctx, http.MethodGet, reqData)
   211  		defer closeResponse(resp)
   212  		if err != nil {
   213  			return nil, err
   214  		}
   215  	}
   216  
   217  	if resp.StatusCode != http.StatusOK {
   218  		return nil, httpRespToErrorResponse(resp)
   219  	}
   220  
   221  	content, err := DecryptData(adm.getSecretKey(), resp.Body)
   222  	if err != nil {
   223  		return nil, err
   224  	}
   225  
   226  	var lst []IDPListItem
   227  	err = json.Unmarshal(content, &lst)
   228  	return lst, err
   229  }
   230  
   231  // DeleteIDPConfig - delete an IDP configuration on the server.
   232  func (adm *AdminClient) DeleteIDPConfig(ctx context.Context, cfgType, cfgName string) (restart bool, err error) {
   233  	if cfgName == "" {
   234  		cfgName = Default
   235  	}
   236  	reqData := requestData{
   237  		relPath: strings.Join([]string{adminAPIPrefix, "idp-config", cfgType, cfgName}, "/"),
   238  	}
   239  
   240  	resp, err := adm.executeMethod(ctx, http.MethodDelete, reqData)
   241  	defer closeResponse(resp)
   242  	if err != nil {
   243  		return false, err
   244  	}
   245  
   246  	// FIXME: Remove support for this older API in 2023-04 (about 6 months).
   247  	//
   248  	// Attempt to fall back to older IDP API.
   249  	if resp.StatusCode == http.StatusUpgradeRequired {
   250  		// close old response
   251  		closeResponse(resp)
   252  
   253  		queryParams := make(url.Values, 2)
   254  		queryParams.Set("type", cfgType)
   255  		queryParams.Set("name", cfgName)
   256  		reqData := requestData{
   257  			relPath:     adminAPIPrefix + "/idp-config",
   258  			queryValues: queryParams,
   259  		}
   260  		resp, err = adm.executeMethod(ctx, http.MethodDelete, reqData)
   261  		defer closeResponse(resp)
   262  		if err != nil {
   263  			return false, err
   264  		}
   265  	}
   266  
   267  	if resp.StatusCode != http.StatusOK {
   268  		return false, httpRespToErrorResponse(resp)
   269  	}
   270  
   271  	return resp.Header.Get(ConfigAppliedHeader) != ConfigAppliedTrue, nil
   272  }
   273  
   274  // PolicyEntitiesResult - contains response to a policy entities query.
   275  type PolicyEntitiesResult struct {
   276  	Timestamp      time.Time             `json:"timestamp"`
   277  	UserMappings   []UserPolicyEntities  `json:"userMappings,omitempty"`
   278  	GroupMappings  []GroupPolicyEntities `json:"groupMappings,omitempty"`
   279  	PolicyMappings []PolicyEntities      `json:"policyMappings,omitempty"`
   280  }
   281  
   282  // UserPolicyEntities - user -> policies mapping
   283  type UserPolicyEntities struct {
   284  	User     string   `json:"user"`
   285  	Policies []string `json:"policies"`
   286  }
   287  
   288  // GroupPolicyEntities - group -> policies mapping
   289  type GroupPolicyEntities struct {
   290  	Group    string   `json:"group"`
   291  	Policies []string `json:"policies"`
   292  }
   293  
   294  // PolicyEntities - policy -> user+group mapping
   295  type PolicyEntities struct {
   296  	Policy string   `json:"policy"`
   297  	Users  []string `json:"users"`
   298  	Groups []string `json:"groups"`
   299  }
   300  
   301  // PolicyEntitiesQuery - contains request info for policy entities query.
   302  type PolicyEntitiesQuery struct {
   303  	Users  []string
   304  	Groups []string
   305  	Policy []string
   306  }
   307  
   308  // GetLDAPPolicyEntities - returns LDAP policy entities.
   309  func (adm *AdminClient) GetLDAPPolicyEntities(ctx context.Context,
   310  	q PolicyEntitiesQuery,
   311  ) (r PolicyEntitiesResult, err error) {
   312  	params := make(url.Values)
   313  	params["user"] = q.Users
   314  	params["group"] = q.Groups
   315  	params["policy"] = q.Policy
   316  
   317  	reqData := requestData{
   318  		relPath:     adminAPIPrefix + "/idp/ldap/policy-entities",
   319  		queryValues: params,
   320  	}
   321  
   322  	resp, err := adm.executeMethod(ctx, http.MethodGet, reqData)
   323  	defer closeResponse(resp)
   324  	if err != nil {
   325  		return r, err
   326  	}
   327  
   328  	if resp.StatusCode != http.StatusOK {
   329  		return r, httpRespToErrorResponse(resp)
   330  	}
   331  
   332  	content, err := DecryptData(adm.getSecretKey(), resp.Body)
   333  	if err != nil {
   334  		return r, err
   335  	}
   336  
   337  	err = json.Unmarshal(content, &r)
   338  	return r, err
   339  }