mirror of
https://github.com/minio/minio.git
synced 2025-01-11 15:03:22 -05:00
fix: enforce deny if present for implicit permissions (#11680)
Implicit permissions for any user is to be allowed to change their own password, we need to restrict this further even if there is an implicit allow for this scenario - we have to honor Deny statements if they are specified.
This commit is contained in:
parent
b1bb3f7016
commit
879599b0cf
@ -24,9 +24,6 @@ jest.mock("jwt-decode")
|
|||||||
jwtDecode.mockImplementation(() => ({ sub: "minio" }))
|
jwtDecode.mockImplementation(() => ({ sub: "minio" }))
|
||||||
|
|
||||||
jest.mock("../../web", () => ({
|
jest.mock("../../web", () => ({
|
||||||
GenerateAuth: jest.fn(() => {
|
|
||||||
return Promise.resolve({ accessKey: "gen1", secretKey: "gen2" })
|
|
||||||
}),
|
|
||||||
SetAuth: jest.fn(
|
SetAuth: jest.fn(
|
||||||
({ currentAccessKey, currentSecretKey, newAccessKey, newSecretKey }) => {
|
({ currentAccessKey, currentSecretKey, newAccessKey, newSecretKey }) => {
|
||||||
if (
|
if (
|
||||||
|
@ -399,7 +399,7 @@ func (a adminAPIHandlers) AddUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
AccountName: parentUser,
|
AccountName: parentUser,
|
||||||
Action: iampolicy.CreateUserAdminAction,
|
Action: iampolicy.CreateUserAdminAction,
|
||||||
ConditionValues: getConditionValues(r, "", parentUser, claims),
|
ConditionValues: getConditionValues(r, "", parentUser, claims),
|
||||||
IsOwner: owner,
|
IsOwner: false,
|
||||||
Claims: claims,
|
Claims: claims,
|
||||||
}) {
|
}) {
|
||||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAccessDenied), r.URL)
|
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAccessDenied), r.URL)
|
||||||
@ -407,6 +407,18 @@ func (a adminAPIHandlers) AddUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if implicitPerm && !globalIAMSys.IsAllowed(iampolicy.Args{
|
||||||
|
AccountName: accessKey,
|
||||||
|
Action: iampolicy.CreateUserAdminAction,
|
||||||
|
ConditionValues: getConditionValues(r, "", accessKey, claims),
|
||||||
|
IsOwner: false,
|
||||||
|
Claims: claims,
|
||||||
|
DenyOnly: true, // check if changing password is explicitly denied.
|
||||||
|
}) {
|
||||||
|
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAccessDenied), r.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if r.ContentLength > maxEConfigJSONSize || r.ContentLength == -1 {
|
if r.ContentLength > maxEConfigJSONSize || r.ContentLength == -1 {
|
||||||
// More than maxConfigSize bytes were available
|
// More than maxConfigSize bytes were available
|
||||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigTooLarge), r.URL)
|
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigTooLarge), r.URL)
|
||||||
|
@ -72,9 +72,7 @@ var matchingFuncNames = [...]string{
|
|||||||
"cmd.(*webAPIHandlers).ListObjects",
|
"cmd.(*webAPIHandlers).ListObjects",
|
||||||
"cmd.(*webAPIHandlers).RemoveObject",
|
"cmd.(*webAPIHandlers).RemoveObject",
|
||||||
"cmd.(*webAPIHandlers).Login",
|
"cmd.(*webAPIHandlers).Login",
|
||||||
"cmd.(*webAPIHandlers).GenerateAuth",
|
|
||||||
"cmd.(*webAPIHandlers).SetAuth",
|
"cmd.(*webAPIHandlers).SetAuth",
|
||||||
"cmd.(*webAPIHandlers).GetAuth",
|
|
||||||
"cmd.(*webAPIHandlers).CreateURLToken",
|
"cmd.(*webAPIHandlers).CreateURLToken",
|
||||||
"cmd.(*webAPIHandlers).Upload",
|
"cmd.(*webAPIHandlers).Upload",
|
||||||
"cmd.(*webAPIHandlers).Download",
|
"cmd.(*webAPIHandlers).Download",
|
||||||
|
@ -78,7 +78,7 @@ func extractBucketObject(args reflect.Value) (bucketName, objectName string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WebGenericArgs - empty struct for calls that don't accept arguments
|
// WebGenericArgs - empty struct for calls that don't accept arguments
|
||||||
// for ex. ServerInfo, GenerateAuth
|
// for ex. ServerInfo
|
||||||
type WebGenericArgs struct{}
|
type WebGenericArgs struct{}
|
||||||
|
|
||||||
// WebGenericRep - reply structure for calls for which reply is success/failure
|
// WebGenericRep - reply structure for calls for which reply is success/failure
|
||||||
@ -981,32 +981,6 @@ func (web *webAPIHandlers) Login(r *http.Request, args *LoginArgs, reply *LoginR
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateAuthReply - reply for GenerateAuth
|
|
||||||
type GenerateAuthReply struct {
|
|
||||||
AccessKey string `json:"accessKey"`
|
|
||||||
SecretKey string `json:"secretKey"`
|
|
||||||
UIVersion string `json:"uiVersion"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (web webAPIHandlers) GenerateAuth(r *http.Request, args *WebGenericArgs, reply *GenerateAuthReply) error {
|
|
||||||
ctx := newWebContext(r, args, "WebGenerateAuth")
|
|
||||||
_, owner, authErr := webRequestAuthenticate(r)
|
|
||||||
if authErr != nil {
|
|
||||||
return toJSONError(ctx, authErr)
|
|
||||||
}
|
|
||||||
if !owner {
|
|
||||||
return toJSONError(ctx, errAccessDenied)
|
|
||||||
}
|
|
||||||
cred, err := auth.GetNewCredentials()
|
|
||||||
if err != nil {
|
|
||||||
return toJSONError(ctx, err)
|
|
||||||
}
|
|
||||||
reply.AccessKey = cred.AccessKey
|
|
||||||
reply.SecretKey = cred.SecretKey
|
|
||||||
reply.UIVersion = browser.UIVersion
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetAuthArgs - argument for SetAuth
|
// SetAuthArgs - argument for SetAuth
|
||||||
type SetAuthArgs struct {
|
type SetAuthArgs struct {
|
||||||
CurrentAccessKey string `json:"currentAccessKey"`
|
CurrentAccessKey string `json:"currentAccessKey"`
|
||||||
@ -1035,6 +1009,17 @@ func (web *webAPIHandlers) SetAuth(r *http.Request, args *SetAuthArgs, reply *Se
|
|||||||
return toJSONError(ctx, errChangeCredNotAllowed)
|
return toJSONError(ctx, errChangeCredNotAllowed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !globalIAMSys.IsAllowed(iampolicy.Args{
|
||||||
|
AccountName: claims.AccessKey,
|
||||||
|
Action: iampolicy.CreateUserAdminAction,
|
||||||
|
IsOwner: false,
|
||||||
|
ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
|
||||||
|
Claims: claims.Map(),
|
||||||
|
DenyOnly: true,
|
||||||
|
}) {
|
||||||
|
return toJSONError(ctx, errChangeCredNotAllowed)
|
||||||
|
}
|
||||||
|
|
||||||
// for IAM users, access key cannot be updated
|
// for IAM users, access key cannot be updated
|
||||||
// claims.AccessKey is used instead of accesskey from args
|
// claims.AccessKey is used instead of accesskey from args
|
||||||
prevCred, ok := globalIAMSys.GetUser(claims.AccessKey)
|
prevCred, ok := globalIAMSys.GetUser(claims.AccessKey)
|
||||||
|
@ -631,43 +631,6 @@ func testRemoveObjectWebHandler(obj ObjectLayer, instanceType string, t TestErrH
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper for calling Generate Auth Handler
|
|
||||||
func TestWebHandlerGenerateAuth(t *testing.T) {
|
|
||||||
ExecObjectLayerTest(t, testGenerateAuthWebHandler)
|
|
||||||
}
|
|
||||||
|
|
||||||
// testGenerateAuthWebHandler - Test GenerateAuth web handler
|
|
||||||
func testGenerateAuthWebHandler(obj ObjectLayer, instanceType string, t TestErrHandler) {
|
|
||||||
// Register the API end points with Erasure/FS object layer.
|
|
||||||
apiRouter := initTestWebRPCEndPoint(obj)
|
|
||||||
credentials := globalActiveCred
|
|
||||||
|
|
||||||
rec := httptest.NewRecorder()
|
|
||||||
authorization, err := getWebRPCToken(apiRouter, credentials.AccessKey, credentials.SecretKey)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Cannot authenticate")
|
|
||||||
}
|
|
||||||
|
|
||||||
generateAuthRequest := WebGenericArgs{}
|
|
||||||
generateAuthReply := &GenerateAuthReply{}
|
|
||||||
req, err := newTestWebRPCRequest("web.GenerateAuth", authorization, generateAuthRequest)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to create HTTP request: <ERROR> %v", err)
|
|
||||||
}
|
|
||||||
apiRouter.ServeHTTP(rec, req)
|
|
||||||
if rec.Code != http.StatusOK {
|
|
||||||
t.Fatalf("Expected the response status to be 200, but instead found `%d`", rec.Code)
|
|
||||||
}
|
|
||||||
err = getTestWebRPCResponse(rec, &generateAuthReply)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed, %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if generateAuthReply.AccessKey == "" || generateAuthReply.SecretKey == "" {
|
|
||||||
t.Fatalf("Failed to generate auth keys")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWebCreateURLToken(t *testing.T) {
|
func TestWebCreateURLToken(t *testing.T) {
|
||||||
ExecObjectLayerTest(t, testCreateURLToken)
|
ExecObjectLayerTest(t, testCreateURLToken)
|
||||||
}
|
}
|
||||||
@ -1134,9 +1097,8 @@ func TestWebCheckAuthorization(t *testing.T) {
|
|||||||
webRPCs := []string{
|
webRPCs := []string{
|
||||||
"ServerInfo", "StorageInfo", "MakeBucket",
|
"ServerInfo", "StorageInfo", "MakeBucket",
|
||||||
"ListBuckets", "ListObjects", "RemoveObject",
|
"ListBuckets", "ListObjects", "RemoveObject",
|
||||||
"GenerateAuth", "SetAuth",
|
"SetAuth", "GetBucketPolicy", "SetBucketPolicy",
|
||||||
"GetBucketPolicy", "SetBucketPolicy", "ListAllBucketPolicies",
|
"ListAllBucketPolicies", "PresignedGet",
|
||||||
"PresignedGet",
|
|
||||||
}
|
}
|
||||||
for _, rpcCall := range webRPCs {
|
for _, rpcCall := range webRPCs {
|
||||||
reply := &WebGenericRep{}
|
reply := &WebGenericRep{}
|
||||||
|
@ -37,6 +37,7 @@ type Args struct {
|
|||||||
IsOwner bool `json:"owner"`
|
IsOwner bool `json:"owner"`
|
||||||
ObjectName string `json:"object"`
|
ObjectName string `json:"object"`
|
||||||
Claims map[string]interface{} `json:"claims"`
|
Claims map[string]interface{} `json:"claims"`
|
||||||
|
DenyOnly bool `json:"denyOnly"` // only applies deny
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPoliciesFromClaims returns the list of policies to be applied for this
|
// GetPoliciesFromClaims returns the list of policies to be applied for this
|
||||||
@ -105,6 +106,15 @@ func (iamp Policy) IsAllowed(args Args) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Applied any 'Deny' only policies, if we have
|
||||||
|
// reached here it means that there were no 'Deny'
|
||||||
|
// policies - this function mainly used for
|
||||||
|
// specific scenarios where we only want to validate
|
||||||
|
// 'Deny' only policies.
|
||||||
|
if args.DenyOnly {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// For owner, its allowed by default.
|
// For owner, its allowed by default.
|
||||||
if args.IsOwner {
|
if args.IsOwner {
|
||||||
return true
|
return true
|
||||||
|
Loading…
Reference in New Issue
Block a user