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:
Aditya Manthramurthy 2021-12-11 09:03:39 -08:00 committed by GitHub
parent 878d368cea
commit 44fefe5b9f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 245 additions and 53 deletions

View File

@ -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

View File

@ -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 {

View File

@ -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
} }

View File

@ -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))
} }

View File

@ -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))
} }

View File

@ -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)
} }
} }
} }

View File

@ -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.

View File

@ -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")