support multiple policies for temporary users (#9550)

This commit is contained in:
Harshavardhana 2020-05-11 13:04:11 -07:00 committed by GitHub
parent 337c2a7cb4
commit f8edc233ab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 86 additions and 29 deletions

View file

@ -1649,7 +1649,7 @@ func toAPIErrorCode(ctx context.Context, err error) (apiErr APIErrorCode) {
if err == nil {
return ErrNone
}
// Verify if the underlying error is signature mismatch.
switch err {
case errInvalidArgument:
apiErr = ErrAdminInvalidArgument

View file

@ -22,6 +22,7 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"strings"
"github.com/minio/minio-go/v6/pkg/set"
"github.com/minio/minio/cmd/config"
@ -166,6 +167,10 @@ type MappedPolicy struct {
Policy string `json:"policy"`
}
func (mp MappedPolicy) policySet() set.StringSet {
return set.CreateStringSet(strings.Split(mp.Policy, ",")...)
}
func newMappedPolicy(policy string) MappedPolicy {
return MappedPolicy{Version: 1, Policy: policy}
}
@ -591,11 +596,26 @@ func (sys *IAMSys) SetTempUser(accessKey string, cred auth.Credentials, policyNa
// temporary user which match with pre-configured canned
// policies for this server.
if globalPolicyOPA == nil && policyName != "" {
p, ok := sys.iamPolicyDocsMap[policyName]
if !ok {
return errInvalidArgument
var availablePolicies []iampolicy.Policy
for _, pname := range strings.Split(policyName, ",") {
pname = strings.TrimSpace(pname)
if pname == "" {
continue
}
p, found := sys.iamPolicyDocsMap[pname]
if !found {
return fmt.Errorf("%w: (%s)", errNoSuchPolicy, pname)
}
availablePolicies = append(availablePolicies, p)
}
if p.IsEmpty() {
combinedPolicy := availablePolicies[0]
for i := 1; i < len(availablePolicies); i++ {
combinedPolicy.Statements = append(combinedPolicy.Statements,
availablePolicies[i].Statements...)
}
if combinedPolicy.IsEmpty() {
delete(sys.iamUserPolicyMap, accessKey)
return nil
}
@ -1265,13 +1285,10 @@ func (sys *IAMSys) PolicyDBSet(name, policy string, isGroup bool) error {
// policyDBSet - sets a policy for user in the policy db. Assumes that caller
// has sys.Lock(). If policy == "", then policy mapping is removed.
func (sys *IAMSys) policyDBSet(name, policy string, userType IAMUserType, isGroup bool) error {
func (sys *IAMSys) policyDBSet(name, policyName string, userType IAMUserType, isGroup bool) error {
if name == "" {
return errInvalidArgument
}
if _, ok := sys.iamPolicyDocsMap[policy]; !ok && policy != "" {
return errNoSuchPolicy
}
if sys.usersSysType == MinIOUsersSysType {
if !isGroup {
@ -1286,7 +1303,7 @@ func (sys *IAMSys) policyDBSet(name, policy string, userType IAMUserType, isGrou
}
// Handle policy mapping removal
if policy == "" {
if policyName == "" {
if err := sys.store.deleteMappedPolicy(name, userType, isGroup); err != nil && err != errNoSuchPolicy {
return err
}
@ -1298,8 +1315,19 @@ func (sys *IAMSys) policyDBSet(name, policy string, userType IAMUserType, isGrou
return nil
}
for _, pname := range strings.Split(policyName, ",") {
pname = strings.TrimSpace(pname)
if pname == "" {
continue
}
if _, found := sys.iamPolicyDocsMap[pname]; !found {
logger.LogIf(GlobalContext, fmt.Errorf("%w: (%s)", errNoSuchPolicy, pname))
return errNoSuchPolicy
}
}
// Handle policy mapping set/update
mp := newMappedPolicy(policy)
mp := newMappedPolicy(policyName)
if err := sys.store.saveMappedPolicy(name, userType, isGroup, mp); err != nil {
return err
}
@ -1644,14 +1672,15 @@ func (sys *IAMSys) IsAllowedSTS(args iampolicy.Args) bool {
return combinedPolicy.IsAllowed(args)
}
pnameSlice, ok := args.GetPolicies(iamPolicyClaimNameOpenID())
policies, ok := args.GetPolicies(iamPolicyClaimNameOpenID())
if !ok {
// When claims are set, it should have a policy claim field.
return false
}
// When claims are set, it should have a policy claim field.
if len(pnameSlice) == 0 {
// When claims are set, it should have policies as claim.
if policies.IsEmpty() {
// No policy, no access!
return false
}
@ -1661,18 +1690,33 @@ func (sys *IAMSys) IsAllowedSTS(args iampolicy.Args) bool {
// If policy is available for given user, check the policy.
mp, ok := sys.iamUserPolicyMap[args.AccountName]
if !ok {
// No policy available reject.
// No policy set for the user that we can find, no access!
return false
}
name := mp.Policy
if pnameSlice[0] != name {
if !policies.Equals(mp.policySet()) {
// When claims has a policy, it should match the
// policy of args.AccountName which server remembers.
// if not reject such requests.
return false
}
var availablePolicies []iampolicy.Policy
for pname := range policies {
p, found := sys.iamPolicyDocsMap[pname]
if !found {
logger.LogIf(GlobalContext, fmt.Errorf("%w: (%s)", errNoSuchPolicy, pname))
return false
}
availablePolicies = append(availablePolicies, p)
}
combinedPolicy := availablePolicies[0]
for i := 1; i < len(availablePolicies); i++ {
combinedPolicy.Statements = append(combinedPolicy.Statements,
availablePolicies[i].Statements...)
}
// Now check if we have a sessionPolicy.
spolicy, ok := args.Claims[iampolicy.SessionPolicyName]
if ok {
@ -1697,14 +1741,12 @@ func (sys *IAMSys) IsAllowedSTS(args iampolicy.Args) bool {
}
// Sub policy is set and valid.
p, ok := sys.iamPolicyDocsMap[pnameSlice[0]]
return ok && p.IsAllowed(args) && subPolicy.IsAllowed(args)
return combinedPolicy.IsAllowed(args) && subPolicy.IsAllowed(args)
}
// Sub policy not set, this is most common since subPolicy
// is optional, use the top level policy only.
p, ok := sys.iamPolicyDocsMap[pnameSlice[0]]
return ok && p.IsAllowed(args)
// is optional, use the inherited policies.
return combinedPolicy.IsAllowed(args)
}
// IsAllowed - checks given policy args is allowed to continue the Rest API.

View file

@ -21,6 +21,7 @@ import (
"io"
"strings"
"github.com/minio/minio-go/v6/pkg/set"
"github.com/minio/minio/pkg/bucket/policy"
)
@ -39,17 +40,31 @@ type Args struct {
}
// GetPolicies get policies
func (a Args) GetPolicies(policyClaimName string) ([]string, bool) {
func (a Args) GetPolicies(policyClaimName string) (set.StringSet, bool) {
s := set.NewStringSet()
pname, ok := a.Claims[policyClaimName]
if !ok {
return nil, false
return s, false
}
pnameStr, ok := pname.(string)
if ok {
return strings.Split(pnameStr, ","), true
pnames, ok := pname.([]string)
if !ok {
pnameStr, ok := pname.(string)
if ok {
pnames = strings.Split(pnameStr, ",")
} else {
return s, false
}
}
pnameSlice, ok := pname.([]string)
return pnameSlice, ok
for _, pname := range pnames {
pname = strings.TrimSpace(pname)
if pname == "" {
// ignore any empty strings, considerate
// towards some user errors.
continue
}
s.Add(pname)
}
return s, true
}
// Policy - iam bucket iamp.