mirror of
https://github.com/minio/minio.git
synced 2024-12-24 06:05:55 -05:00
Add option to policy info API to return create/mod timestamps (#13796)
- This introduces a new admin API with a query parameter (v=2) to return a response with the timestamps - Older API still works for compatibility/smooth transition in console
This commit is contained in:
parent
878d368cea
commit
44fefe5b9f
@ -1198,6 +1198,16 @@ func (a adminAPIHandlers) AccountInfoHandler(w http.ResponseWriter, r *http.Requ
|
|||||||
}
|
}
|
||||||
|
|
||||||
// InfoCannedPolicy - GET /minio/admin/v3/info-canned-policy?name={policyName}
|
// InfoCannedPolicy - GET /minio/admin/v3/info-canned-policy?name={policyName}
|
||||||
|
//
|
||||||
|
// Newer API response with policy timestamps is returned with query parameter
|
||||||
|
// `v=2` like:
|
||||||
|
//
|
||||||
|
// GET /minio/admin/v3/info-canned-policy?name={policyName}&v=2
|
||||||
|
//
|
||||||
|
// The newer API will eventually become the default (and only) one. The older
|
||||||
|
// response is to return only the policy JSON. The newer response returns
|
||||||
|
// timestamps along with the policy JSON. Both versions are supported for now,
|
||||||
|
// for smooth transition to new API.
|
||||||
func (a adminAPIHandlers) InfoCannedPolicy(w http.ResponseWriter, r *http.Request) {
|
func (a adminAPIHandlers) InfoCannedPolicy(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "InfoCannedPolicy")
|
ctx := newContext(r, w, "InfoCannedPolicy")
|
||||||
|
|
||||||
@ -1208,13 +1218,36 @@ func (a adminAPIHandlers) InfoCannedPolicy(w http.ResponseWriter, r *http.Reques
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
policy, err := globalIAMSys.InfoPolicy(mux.Vars(r)["name"])
|
name := mux.Vars(r)["name"]
|
||||||
|
policies := newMappedPolicy(name).toSlice()
|
||||||
|
if len(policies) != 1 {
|
||||||
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, errTooManyPolicies), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
policyDoc, err := globalIAMSys.InfoPolicy(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
buf, err := json.MarshalIndent(policy, "", " ")
|
// Is the new API version being requested?
|
||||||
|
infoPolicyAPIVersion := r.Form.Get("v")
|
||||||
|
if infoPolicyAPIVersion == "2" {
|
||||||
|
buf, err := json.MarshalIndent(policyDoc, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Write(buf)
|
||||||
|
return
|
||||||
|
} else if infoPolicyAPIVersion != "" {
|
||||||
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, errors.New("invalid version parameter 'v' supplied")), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the older API response value of just the policy json.
|
||||||
|
buf, err := json.MarshalIndent(policyDoc.Policy, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||||
return
|
return
|
||||||
|
@ -29,26 +29,31 @@ import (
|
|||||||
|
|
||||||
var errConfigNotFound = errors.New("config file not found")
|
var errConfigNotFound = errors.New("config file not found")
|
||||||
|
|
||||||
func readConfig(ctx context.Context, objAPI ObjectLayer, configFile string) ([]byte, error) {
|
func readConfigWithMetadata(ctx context.Context, objAPI ObjectLayer, configFile string) ([]byte, ObjectInfo, error) {
|
||||||
r, err := objAPI.GetObjectNInfo(ctx, minioMetaBucket, configFile, nil, http.Header{}, readLock, ObjectOptions{})
|
r, err := objAPI.GetObjectNInfo(ctx, minioMetaBucket, configFile, nil, http.Header{}, readLock, ObjectOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Treat object not found as config not found.
|
// Treat object not found as config not found.
|
||||||
if isErrObjectNotFound(err) {
|
if isErrObjectNotFound(err) {
|
||||||
return nil, errConfigNotFound
|
return nil, ObjectInfo{}, errConfigNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, err
|
return nil, ObjectInfo{}, err
|
||||||
}
|
}
|
||||||
defer r.Close()
|
defer r.Close()
|
||||||
|
|
||||||
buf, err := ioutil.ReadAll(r)
|
buf, err := ioutil.ReadAll(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, ObjectInfo{}, err
|
||||||
}
|
}
|
||||||
if len(buf) == 0 {
|
if len(buf) == 0 {
|
||||||
return nil, errConfigNotFound
|
return nil, ObjectInfo{}, errConfigNotFound
|
||||||
}
|
}
|
||||||
return buf, nil
|
return buf, r.ObjInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readConfig(ctx context.Context, objAPI ObjectLayer, configFile string) ([]byte, error) {
|
||||||
|
buf, _, err := readConfigWithMetadata(ctx, objAPI, configFile)
|
||||||
|
return buf, err
|
||||||
}
|
}
|
||||||
|
|
||||||
type objectDeleter interface {
|
type objectDeleter interface {
|
||||||
|
@ -22,7 +22,6 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/minio/minio/internal/auth"
|
"github.com/minio/minio/internal/auth"
|
||||||
iampolicy "github.com/minio/pkg/iam/policy"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type iamDummyStore struct {
|
type iamDummyStore struct {
|
||||||
@ -64,7 +63,7 @@ func (ids *iamDummyStore) migrateBackendFormat(context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ids *iamDummyStore) loadPolicyDoc(ctx context.Context, policy string, m map[string]iampolicy.Policy) error {
|
func (ids *iamDummyStore) loadPolicyDoc(ctx context.Context, policy string, m map[string]PolicyDoc) error {
|
||||||
v, ok := ids.iamPolicyDocsMap[policy]
|
v, ok := ids.iamPolicyDocsMap[policy]
|
||||||
if !ok {
|
if !ok {
|
||||||
return errNoSuchPolicy
|
return errNoSuchPolicy
|
||||||
@ -73,7 +72,7 @@ func (ids *iamDummyStore) loadPolicyDoc(ctx context.Context, policy string, m ma
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ids *iamDummyStore) loadPolicyDocs(ctx context.Context, m map[string]iampolicy.Policy) error {
|
func (ids *iamDummyStore) loadPolicyDocs(ctx context.Context, m map[string]PolicyDoc) error {
|
||||||
for k, v := range ids.iamPolicyDocsMap {
|
for k, v := range ids.iamPolicyDocsMap {
|
||||||
m[k] = v
|
m[k] = v
|
||||||
}
|
}
|
||||||
@ -154,7 +153,7 @@ func (ids *iamDummyStore) deleteIAMConfig(ctx context.Context, path string) erro
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ids *iamDummyStore) savePolicyDoc(ctx context.Context, policyName string, p iampolicy.Policy) error {
|
func (ids *iamDummyStore) savePolicyDoc(ctx context.Context, policyName string, p PolicyDoc) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,6 @@ import (
|
|||||||
"github.com/minio/minio/internal/config"
|
"github.com/minio/minio/internal/config"
|
||||||
"github.com/minio/minio/internal/kms"
|
"github.com/minio/minio/internal/kms"
|
||||||
"github.com/minio/minio/internal/logger"
|
"github.com/minio/minio/internal/logger"
|
||||||
iampolicy "github.com/minio/pkg/iam/policy"
|
|
||||||
"go.etcd.io/etcd/api/v3/mvccpb"
|
"go.etcd.io/etcd/api/v3/mvccpb"
|
||||||
etcd "go.etcd.io/etcd/client/v3"
|
etcd "go.etcd.io/etcd/client/v3"
|
||||||
)
|
)
|
||||||
@ -115,7 +114,7 @@ func (ies *IAMEtcdStore) saveIAMConfig(ctx context.Context, item interface{}, it
|
|||||||
return saveKeyEtcd(ctx, ies.client, itemPath, data, opts...)
|
return saveKeyEtcd(ctx, ies.client, itemPath, data, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getIAMConfig(item interface{}, data []byte, itemPath string) error {
|
func decryptData(data []byte, itemPath string) ([]byte, error) {
|
||||||
var err error
|
var err error
|
||||||
if !utf8.Valid(data) && GlobalKMS != nil {
|
if !utf8.Valid(data) && GlobalKMS != nil {
|
||||||
data, err = config.DecryptBytes(GlobalKMS, data, kms.Context{
|
data, err = config.DecryptBytes(GlobalKMS, data, kms.Context{
|
||||||
@ -128,10 +127,18 @@ func getIAMConfig(item interface{}, data []byte, itemPath string) error {
|
|||||||
minioMetaBucket: itemPath,
|
minioMetaBucket: itemPath,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIAMConfig(item interface{}, data []byte, itemPath string) error {
|
||||||
|
data, err := decryptData(data, itemPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||||
return json.Unmarshal(data, item)
|
return json.Unmarshal(data, item)
|
||||||
}
|
}
|
||||||
@ -144,6 +151,14 @@ func (ies *IAMEtcdStore) loadIAMConfig(ctx context.Context, item interface{}, pa
|
|||||||
return getIAMConfig(item, data, path)
|
return getIAMConfig(item, data, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ies *IAMEtcdStore) loadIAMConfigBytes(ctx context.Context, path string) ([]byte, error) {
|
||||||
|
data, err := readKeyEtcd(ctx, ies.client, path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return decryptData(data, path)
|
||||||
|
}
|
||||||
|
|
||||||
func (ies *IAMEtcdStore) deleteIAMConfig(ctx context.Context, path string) error {
|
func (ies *IAMEtcdStore) deleteIAMConfig(ctx context.Context, path string) error {
|
||||||
return deleteKeyEtcd(ctx, ies.client, path)
|
return deleteKeyEtcd(ctx, ies.client, path)
|
||||||
}
|
}
|
||||||
@ -263,34 +278,46 @@ func (ies *IAMEtcdStore) migrateBackendFormat(ctx context.Context) error {
|
|||||||
return ies.migrateToV1(ctx)
|
return ies.migrateToV1(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ies *IAMEtcdStore) loadPolicyDoc(ctx context.Context, policy string, m map[string]iampolicy.Policy) error {
|
func (ies *IAMEtcdStore) loadPolicyDoc(ctx context.Context, policy string, m map[string]PolicyDoc) error {
|
||||||
var p iampolicy.Policy
|
data, err := ies.loadIAMConfigBytes(ctx, getPolicyDocPath(policy))
|
||||||
err := ies.loadIAMConfig(ctx, &p, getPolicyDocPath(policy))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == errConfigNotFound {
|
if err == errConfigNotFound {
|
||||||
return errNoSuchPolicy
|
return errNoSuchPolicy
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var p PolicyDoc
|
||||||
|
err = p.parseJSON(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
m[policy] = p
|
m[policy] = p
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ies *IAMEtcdStore) getPolicyDocKV(ctx context.Context, kvs *mvccpb.KeyValue, m map[string]iampolicy.Policy) error {
|
func (ies *IAMEtcdStore) getPolicyDocKV(ctx context.Context, kvs *mvccpb.KeyValue, m map[string]PolicyDoc) error {
|
||||||
var p iampolicy.Policy
|
data, err := decryptData(kvs.Value, string(kvs.Key))
|
||||||
err := getIAMConfig(&p, kvs.Value, string(kvs.Key))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == errConfigNotFound {
|
if err == errConfigNotFound {
|
||||||
return errNoSuchPolicy
|
return errNoSuchPolicy
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var p PolicyDoc
|
||||||
|
err = p.parseJSON(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
policy := extractPathPrefixAndSuffix(string(kvs.Key), iamConfigPoliciesPrefix, path.Base(string(kvs.Key)))
|
policy := extractPathPrefixAndSuffix(string(kvs.Key), iamConfigPoliciesPrefix, path.Base(string(kvs.Key)))
|
||||||
m[policy] = p
|
m[policy] = p
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ies *IAMEtcdStore) loadPolicyDocs(ctx context.Context, m map[string]iampolicy.Policy) error {
|
func (ies *IAMEtcdStore) loadPolicyDocs(ctx context.Context, m map[string]PolicyDoc) error {
|
||||||
ctx, cancel := context.WithTimeout(ctx, defaultContextTimeout)
|
ctx, cancel := context.WithTimeout(ctx, defaultContextTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
// Retrieve all keys and values to avoid too many calls to etcd in case of
|
// Retrieve all keys and values to avoid too many calls to etcd in case of
|
||||||
@ -473,7 +500,7 @@ func (ies *IAMEtcdStore) loadMappedPolicies(ctx context.Context, userType IAMUse
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ies *IAMEtcdStore) savePolicyDoc(ctx context.Context, policyName string, p iampolicy.Policy) error {
|
func (ies *IAMEtcdStore) savePolicyDoc(ctx context.Context, policyName string, p PolicyDoc) error {
|
||||||
return ies.saveIAMConfig(ctx, &p, getPolicyDocPath(policyName))
|
return ies.saveIAMConfig(ctx, &p, getPolicyDocPath(policyName))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,6 @@ import (
|
|||||||
"github.com/minio/minio/internal/config"
|
"github.com/minio/minio/internal/config"
|
||||||
"github.com/minio/minio/internal/kms"
|
"github.com/minio/minio/internal/kms"
|
||||||
"github.com/minio/minio/internal/logger"
|
"github.com/minio/minio/internal/logger"
|
||||||
iampolicy "github.com/minio/pkg/iam/policy"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// IAMObjectStore implements IAMStorageAPI
|
// IAMObjectStore implements IAMStorageAPI
|
||||||
@ -218,19 +217,27 @@ func (iamOS *IAMObjectStore) saveIAMConfig(ctx context.Context, item interface{}
|
|||||||
return saveConfig(ctx, iamOS.objAPI, objPath, data)
|
return saveConfig(ctx, iamOS.objAPI, objPath, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iamOS *IAMObjectStore) loadIAMConfig(ctx context.Context, item interface{}, objPath string) error {
|
func (iamOS *IAMObjectStore) loadIAMConfigBytesWithMetadata(ctx context.Context, objPath string) ([]byte, ObjectInfo, error) {
|
||||||
data, err := readConfig(ctx, iamOS.objAPI, objPath)
|
data, meta, err := readConfigWithMetadata(ctx, iamOS.objAPI, objPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, meta, err
|
||||||
}
|
}
|
||||||
if !utf8.Valid(data) && GlobalKMS != nil {
|
if !utf8.Valid(data) && GlobalKMS != nil {
|
||||||
data, err = config.DecryptBytes(GlobalKMS, data, kms.Context{
|
data, err = config.DecryptBytes(GlobalKMS, data, kms.Context{
|
||||||
minioMetaBucket: path.Join(minioMetaBucket, objPath),
|
minioMetaBucket: path.Join(minioMetaBucket, objPath),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, meta, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return data, meta, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iamOS *IAMObjectStore) loadIAMConfig(ctx context.Context, item interface{}, objPath string) error {
|
||||||
|
data, _, err := iamOS.loadIAMConfigBytesWithMetadata(ctx, objPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||||
return json.Unmarshal(data, item)
|
return json.Unmarshal(data, item)
|
||||||
}
|
}
|
||||||
@ -239,20 +246,34 @@ func (iamOS *IAMObjectStore) deleteIAMConfig(ctx context.Context, path string) e
|
|||||||
return deleteConfig(ctx, iamOS.objAPI, path)
|
return deleteConfig(ctx, iamOS.objAPI, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iamOS *IAMObjectStore) loadPolicyDoc(ctx context.Context, policy string, m map[string]iampolicy.Policy) error {
|
func (iamOS *IAMObjectStore) loadPolicyDoc(ctx context.Context, policy string, m map[string]PolicyDoc) error {
|
||||||
var p iampolicy.Policy
|
data, objInfo, err := iamOS.loadIAMConfigBytesWithMetadata(ctx, getPolicyDocPath(policy))
|
||||||
err := iamOS.loadIAMConfig(ctx, &p, getPolicyDocPath(policy))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == errConfigNotFound {
|
if err == errConfigNotFound {
|
||||||
return errNoSuchPolicy
|
return errNoSuchPolicy
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var p PolicyDoc
|
||||||
|
err = p.parseJSON(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.Version == 0 {
|
||||||
|
// This means that policy was in the old version (without any
|
||||||
|
// timestamp info). We fetch the mod time of the file and save
|
||||||
|
// that as create and update date.
|
||||||
|
p.CreateDate = objInfo.ModTime
|
||||||
|
p.UpdateDate = objInfo.ModTime
|
||||||
|
}
|
||||||
|
|
||||||
m[policy] = p
|
m[policy] = p
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iamOS *IAMObjectStore) loadPolicyDocs(ctx context.Context, m map[string]iampolicy.Policy) error {
|
func (iamOS *IAMObjectStore) loadPolicyDocs(ctx context.Context, m map[string]PolicyDoc) error {
|
||||||
for item := range listIAMConfigItems(ctx, iamOS.objAPI, iamConfigPoliciesPrefix) {
|
for item := range listIAMConfigItems(ctx, iamOS.objAPI, iamConfigPoliciesPrefix) {
|
||||||
if item.Err != nil {
|
if item.Err != nil {
|
||||||
return item.Err
|
return item.Err
|
||||||
@ -385,7 +406,7 @@ func (iamOS *IAMObjectStore) loadMappedPolicies(ctx context.Context, userType IA
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iamOS *IAMObjectStore) savePolicyDoc(ctx context.Context, policyName string, p iampolicy.Policy) error {
|
func (iamOS *IAMObjectStore) savePolicyDoc(ctx context.Context, policyName string, p PolicyDoc) error {
|
||||||
return iamOS.saveIAMConfig(ctx, &p, getPolicyDocPath(policyName))
|
return iamOS.saveIAMConfig(ctx, &p, getPolicyDocPath(policyName))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
120
cmd/iam-store.go
120
cmd/iam-store.go
@ -24,8 +24,10 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/dustin/go-humanize"
|
"github.com/dustin/go-humanize"
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
"github.com/minio/madmin-go"
|
"github.com/minio/madmin-go"
|
||||||
"github.com/minio/minio-go/v7/pkg/set"
|
"github.com/minio/minio-go/v7/pkg/set"
|
||||||
"github.com/minio/minio/internal/auth"
|
"github.com/minio/minio/internal/auth"
|
||||||
@ -169,6 +171,64 @@ func newMappedPolicy(policy string) MappedPolicy {
|
|||||||
return MappedPolicy{Version: 1, Policies: policy}
|
return MappedPolicy{Version: 1, Policies: policy}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PolicyDoc represents an IAM policy with some metadata.
|
||||||
|
type PolicyDoc struct {
|
||||||
|
Version int `json:",omitempty"`
|
||||||
|
Policy iampolicy.Policy
|
||||||
|
CreateDate time.Time `json:",omitempty"`
|
||||||
|
UpdateDate time.Time `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPolicyDoc(p iampolicy.Policy) PolicyDoc {
|
||||||
|
now := UTCNow().Round(time.Millisecond)
|
||||||
|
return PolicyDoc{
|
||||||
|
Version: 1,
|
||||||
|
Policy: p,
|
||||||
|
CreateDate: now,
|
||||||
|
UpdateDate: now,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultPolicyDoc - used to wrap a default policy as PolicyDoc.
|
||||||
|
func defaultPolicyDoc(p iampolicy.Policy) PolicyDoc {
|
||||||
|
return PolicyDoc{
|
||||||
|
Version: 1,
|
||||||
|
Policy: p,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *PolicyDoc) update(p iampolicy.Policy) {
|
||||||
|
now := UTCNow().Round(time.Millisecond)
|
||||||
|
d.UpdateDate = now
|
||||||
|
if d.CreateDate.IsZero() {
|
||||||
|
d.CreateDate = now
|
||||||
|
}
|
||||||
|
d.Policy = p
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseJSON parses both the old and the new format for storing policy
|
||||||
|
// definitions.
|
||||||
|
//
|
||||||
|
// The on-disk format of policy definitions has changed (around early 12/2021)
|
||||||
|
// from iampolicy.Policy to PolicyDoc. To avoid a migration, loading supports
|
||||||
|
// both the old and the new formats.
|
||||||
|
func (d *PolicyDoc) parseJSON(data []byte) error {
|
||||||
|
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||||
|
var doc PolicyDoc
|
||||||
|
err := json.Unmarshal(data, &doc)
|
||||||
|
if err != nil {
|
||||||
|
err2 := json.Unmarshal(data, &doc.Policy)
|
||||||
|
if err2 != nil {
|
||||||
|
// Just return the first error.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.Policy = doc.Policy
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
*d = doc
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// key options
|
// key options
|
||||||
type options struct {
|
type options struct {
|
||||||
ttl int64 // expiry in seconds
|
ttl int64 // expiry in seconds
|
||||||
@ -182,7 +242,7 @@ type iamWatchEvent struct {
|
|||||||
// iamCache contains in-memory cache of IAM data.
|
// iamCache contains in-memory cache of IAM data.
|
||||||
type iamCache struct {
|
type iamCache struct {
|
||||||
// map of policy names to policy definitions
|
// map of policy names to policy definitions
|
||||||
iamPolicyDocsMap map[string]iampolicy.Policy
|
iamPolicyDocsMap map[string]PolicyDoc
|
||||||
// map of usernames to credentials
|
// map of usernames to credentials
|
||||||
iamUsersMap map[string]auth.Credentials
|
iamUsersMap map[string]auth.Credentials
|
||||||
// map of group names to group info
|
// map of group names to group info
|
||||||
@ -197,7 +257,7 @@ type iamCache struct {
|
|||||||
|
|
||||||
func newIamCache() *iamCache {
|
func newIamCache() *iamCache {
|
||||||
return &iamCache{
|
return &iamCache{
|
||||||
iamPolicyDocsMap: map[string]iampolicy.Policy{},
|
iamPolicyDocsMap: map[string]PolicyDoc{},
|
||||||
iamUsersMap: map[string]auth.Credentials{},
|
iamUsersMap: map[string]auth.Credentials{},
|
||||||
iamGroupsMap: map[string]GroupInfo{},
|
iamGroupsMap: map[string]GroupInfo{},
|
||||||
iamUserGroupMemberships: map[string]set.StringSet{},
|
iamUserGroupMemberships: map[string]set.StringSet{},
|
||||||
@ -332,8 +392,8 @@ type IAMStorageAPI interface {
|
|||||||
|
|
||||||
getUsersSysType() UsersSysType
|
getUsersSysType() UsersSysType
|
||||||
|
|
||||||
loadPolicyDoc(ctx context.Context, policy string, m map[string]iampolicy.Policy) error
|
loadPolicyDoc(ctx context.Context, policy string, m map[string]PolicyDoc) error
|
||||||
loadPolicyDocs(ctx context.Context, m map[string]iampolicy.Policy) error
|
loadPolicyDocs(ctx context.Context, m map[string]PolicyDoc) error
|
||||||
|
|
||||||
loadUser(ctx context.Context, user string, userType IAMUserType, m map[string]auth.Credentials) error
|
loadUser(ctx context.Context, user string, userType IAMUserType, m map[string]auth.Credentials) error
|
||||||
loadUsers(ctx context.Context, userType IAMUserType, m map[string]auth.Credentials) error
|
loadUsers(ctx context.Context, userType IAMUserType, m map[string]auth.Credentials) error
|
||||||
@ -348,7 +408,7 @@ type IAMStorageAPI interface {
|
|||||||
loadIAMConfig(ctx context.Context, item interface{}, path string) error
|
loadIAMConfig(ctx context.Context, item interface{}, path string) error
|
||||||
deleteIAMConfig(ctx context.Context, path string) error
|
deleteIAMConfig(ctx context.Context, path string) error
|
||||||
|
|
||||||
savePolicyDoc(ctx context.Context, policyName string, p iampolicy.Policy) error
|
savePolicyDoc(ctx context.Context, policyName string, p PolicyDoc) error
|
||||||
saveMappedPolicy(ctx context.Context, name string, userType IAMUserType, isGroup bool, mp MappedPolicy, opts ...options) error
|
saveMappedPolicy(ctx context.Context, name string, userType IAMUserType, isGroup bool, mp MappedPolicy, opts ...options) error
|
||||||
saveUserIdentity(ctx context.Context, name string, userType IAMUserType, u UserIdentity, opts ...options) error
|
saveUserIdentity(ctx context.Context, name string, userType IAMUserType, u UserIdentity, opts ...options) error
|
||||||
saveGroupInfo(ctx context.Context, group string, gi GroupInfo) error
|
saveGroupInfo(ctx context.Context, group string, gi GroupInfo) error
|
||||||
@ -366,10 +426,10 @@ type iamStorageWatcher interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set default canned policies only if not already overridden by users.
|
// Set default canned policies only if not already overridden by users.
|
||||||
func setDefaultCannedPolicies(policies map[string]iampolicy.Policy) {
|
func setDefaultCannedPolicies(policies map[string]PolicyDoc) {
|
||||||
for _, v := range iampolicy.DefaultPolicies {
|
for _, v := range iampolicy.DefaultPolicies {
|
||||||
if _, ok := policies[v.Name]; !ok {
|
if _, ok := policies[v.Name]; !ok {
|
||||||
policies[v.Name] = v.Definition
|
policies[v.Name] = defaultPolicyDoc(v.Definition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -947,13 +1007,31 @@ func (store *IAMStoreSys) GetPolicy(name string) (iampolicy.Policy, error) {
|
|||||||
}
|
}
|
||||||
v, ok := cache.iamPolicyDocsMap[policy]
|
v, ok := cache.iamPolicyDocsMap[policy]
|
||||||
if !ok {
|
if !ok {
|
||||||
return v, errNoSuchPolicy
|
return v.Policy, errNoSuchPolicy
|
||||||
}
|
}
|
||||||
combinedPolicy = combinedPolicy.Merge(v)
|
combinedPolicy = combinedPolicy.Merge(v.Policy)
|
||||||
}
|
}
|
||||||
return combinedPolicy, nil
|
return combinedPolicy, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPolicyDoc - gets the policy doc which has the policy and some metadata.
|
||||||
|
// Exactly one policy must be specified here.
|
||||||
|
func (store *IAMStoreSys) GetPolicyDoc(name string) (r PolicyDoc, err error) {
|
||||||
|
name = strings.TrimSpace(name)
|
||||||
|
if name == "" {
|
||||||
|
return r, errInvalidArgument
|
||||||
|
}
|
||||||
|
|
||||||
|
cache := store.rlock()
|
||||||
|
defer store.runlock()
|
||||||
|
|
||||||
|
v, ok := cache.iamPolicyDocsMap[name]
|
||||||
|
if !ok {
|
||||||
|
return r, errNoSuchPolicy
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
// SetPolicy - creates a policy with name.
|
// SetPolicy - creates a policy with name.
|
||||||
func (store *IAMStoreSys) SetPolicy(ctx context.Context, name string, policy iampolicy.Policy) error {
|
func (store *IAMStoreSys) SetPolicy(ctx context.Context, name string, policy iampolicy.Policy) error {
|
||||||
|
|
||||||
@ -964,11 +1042,21 @@ func (store *IAMStoreSys) SetPolicy(ctx context.Context, name string, policy iam
|
|||||||
cache := store.lock()
|
cache := store.lock()
|
||||||
defer store.unlock()
|
defer store.unlock()
|
||||||
|
|
||||||
if err := store.savePolicyDoc(ctx, name, policy); err != nil {
|
var (
|
||||||
|
d PolicyDoc
|
||||||
|
ok bool
|
||||||
|
)
|
||||||
|
if d, ok = cache.iamPolicyDocsMap[name]; ok {
|
||||||
|
d.update(policy)
|
||||||
|
} else {
|
||||||
|
d = newPolicyDoc(policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := store.savePolicyDoc(ctx, name, d); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cache.iamPolicyDocsMap[name] = policy
|
cache.iamPolicyDocsMap[name] = d
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -979,7 +1067,7 @@ func (store *IAMStoreSys) ListPolicies(ctx context.Context, bucketName string) (
|
|||||||
cache := store.lock()
|
cache := store.lock()
|
||||||
defer store.unlock()
|
defer store.unlock()
|
||||||
|
|
||||||
m := map[string]iampolicy.Policy{}
|
m := map[string]PolicyDoc{}
|
||||||
err := store.loadPolicyDocs(ctx, m)
|
err := store.loadPolicyDocs(ctx, m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -992,8 +1080,8 @@ func (store *IAMStoreSys) ListPolicies(ctx context.Context, bucketName string) (
|
|||||||
|
|
||||||
ret := map[string]iampolicy.Policy{}
|
ret := map[string]iampolicy.Policy{}
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
if bucketName == "" || v.MatchResource(bucketName) {
|
if bucketName == "" || v.Policy.MatchResource(bucketName) {
|
||||||
ret[k] = v
|
ret[k] = v.Policy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1011,9 +1099,9 @@ func filterPolicies(cache *iamCache, policyName string, bucketName string) (stri
|
|||||||
}
|
}
|
||||||
p, found := cache.iamPolicyDocsMap[policy]
|
p, found := cache.iamPolicyDocsMap[policy]
|
||||||
if found {
|
if found {
|
||||||
if bucketName == "" || p.MatchResource(bucketName) {
|
if bucketName == "" || p.Policy.MatchResource(bucketName) {
|
||||||
policies = append(policies, policy)
|
policies = append(policies, policy)
|
||||||
combinedPolicy = combinedPolicy.Merge(p)
|
combinedPolicy = combinedPolicy.Merge(p.Policy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
23
cmd/iam.go
23
cmd/iam.go
@ -477,13 +477,28 @@ func (sys *IAMSys) DeletePolicy(ctx context.Context, policyName string, notifyPe
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// InfoPolicy - expands the canned policy into its JSON structure.
|
// InfoPolicy - returns the policy definition with some metadata.
|
||||||
func (sys *IAMSys) InfoPolicy(policyName string) (iampolicy.Policy, error) {
|
func (sys *IAMSys) InfoPolicy(policyName string) (*madmin.PolicyInfo, error) {
|
||||||
if !sys.Initialized() {
|
if !sys.Initialized() {
|
||||||
return iampolicy.Policy{}, errServerNotInitialized
|
return nil, errServerNotInitialized
|
||||||
}
|
}
|
||||||
|
|
||||||
return sys.store.GetPolicy(policyName)
|
d, err := sys.store.GetPolicyDoc(policyName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pdata, err := json.Marshal(d.Policy)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &madmin.PolicyInfo{
|
||||||
|
PolicyName: policyName,
|
||||||
|
Policy: pdata,
|
||||||
|
CreateDate: d.CreateDate,
|
||||||
|
UpdateDate: d.UpdateDate,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListPolicies - lists all canned policies.
|
// ListPolicies - lists all canned policies.
|
||||||
|
@ -84,6 +84,10 @@ var errNoSuchPolicy = errors.New("Specified canned policy does not exist")
|
|||||||
// error returned when policy to be deleted is in use.
|
// error returned when policy to be deleted is in use.
|
||||||
var errPolicyInUse = errors.New("Specified policy is in use and cannot be deleted.")
|
var errPolicyInUse = errors.New("Specified policy is in use and cannot be deleted.")
|
||||||
|
|
||||||
|
// error returned when more than a single policy is specified when only one is
|
||||||
|
// expectd.
|
||||||
|
var errTooManyPolicies = errors.New("Only a single policy may be specified here.")
|
||||||
|
|
||||||
// error returned in IAM subsystem when an external users systems is configured.
|
// error returned in IAM subsystem when an external users systems is configured.
|
||||||
var errIAMActionNotAllowed = errors.New("Specified IAM action is not allowed")
|
var errIAMActionNotAllowed = errors.New("Specified IAM action is not allowed")
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user